import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

1039
gfx/thebes/CJKCompatSVS.cpp Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,137 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ContextStateTracker.h"
#include "GLContext.h"
#ifdef MOZ_ENABLE_PROFILER_SPS
#include "ProfilerMarkers.h"
#endif
namespace mozilla {
void
ContextStateTrackerOGL::PushOGLSection(GLContext* aGL, const char* aSectionName)
{
if (!profiler_feature_active("gpu")) {
return;
}
if (!aGL->IsSupported(gl::GLFeature::query_objects)) {
return;
}
if (mSectionStack.Length() > 0) {
// We need to end the query since we're starting a new section and restore it
// when this section is finished.
aGL->fEndQuery(LOCAL_GL_TIME_ELAPSED);
Top().mCpuTimeEnd = TimeStamp::Now();
}
ContextState newSection(aSectionName);
GLuint queryObject;
aGL->fGenQueries(1, &queryObject);
newSection.mStartQueryHandle = queryObject;
newSection.mCpuTimeStart = TimeStamp::Now();
aGL->fBeginQuery(LOCAL_GL_TIME_ELAPSED_EXT, queryObject);
mSectionStack.AppendElement(newSection);
}
void
ContextStateTrackerOGL::PopOGLSection(GLContext* aGL, const char* aSectionName)
{
// We might have ignored a section start if we started profiling
// in the middle section. If so we will ignore this unmatched end.
if (mSectionStack.Length() == 0) {
return;
}
int i = mSectionStack.Length() - 1;
MOZ_ASSERT(strcmp(mSectionStack[i].mSectionName, aSectionName) == 0);
aGL->fEndQuery(LOCAL_GL_TIME_ELAPSED);
mSectionStack[i].mCpuTimeEnd = TimeStamp::Now();
mCompletedSections.AppendElement(mSectionStack[i]);
mSectionStack.RemoveElementAt(i);
if (i - 1 >= 0) {
const char* sectionToRestore = Top().mSectionName;
// We need to restore the outer section
// Well do this by completing this section and adding a new
// one with the same name
mCompletedSections.AppendElement(Top());
mSectionStack.RemoveElementAt(i - 1);
ContextState newSection(sectionToRestore);
GLuint queryObject;
aGL->fGenQueries(1, &queryObject);
newSection.mStartQueryHandle = queryObject;
newSection.mCpuTimeStart = TimeStamp::Now();
aGL->fBeginQuery(LOCAL_GL_TIME_ELAPSED_EXT, queryObject);
mSectionStack.AppendElement(newSection);
}
Flush(aGL);
}
void
ContextStateTrackerOGL::Flush(GLContext* aGL)
{
TimeStamp now = TimeStamp::Now();
while (mCompletedSections.Length() != 0) {
// On mac we see QUERY_RESULT_AVAILABLE cause a GL flush if we query it
// too early. For profiling we rather have the last 200ms of data missing
// while causing let's measurement distortions.
if (mCompletedSections[0].mCpuTimeEnd + TimeDuration::FromMilliseconds(200) > now) {
break;
}
GLuint handle = mCompletedSections[0].mStartQueryHandle;
// We've waiting 200ms, content rendering at > 20 FPS will be ready. We
// shouldn't see any flushes now.
GLuint returned = 0;
aGL->fGetQueryObjectuiv(handle, LOCAL_GL_QUERY_RESULT_AVAILABLE, &returned);
if (!returned) {
break;
}
GLuint gpuTime = 0;
aGL->fGetQueryObjectuiv(handle, LOCAL_GL_QUERY_RESULT, &gpuTime);
aGL->fDeleteQueries(1, &handle);
#ifdef MOZ_ENABLE_PROFILER_SPS
PROFILER_MARKER_PAYLOAD("gpu_timer_query", new GPUMarkerPayload(
mCompletedSections[0].mCpuTimeStart,
mCompletedSections[0].mCpuTimeEnd,
0,
gpuTime
));
#endif
mCompletedSections.RemoveElementAt(0);
}
}
void
ContextStateTrackerOGL::DestroyOGL(GLContext* aGL)
{
while (mCompletedSections.Length() != 0) {
GLuint handle = (GLuint)mCompletedSections[0].mStartQueryHandle;
aGL->fDeleteQueries(1, &handle);
mCompletedSections.RemoveElementAt(0);
}
}
} // namespace mozilla

View file

@ -0,0 +1,84 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_CONTEXTSTATETRACKER_H
#define GFX_CONTEXTSTATETRACKER_H
#include "GLTypes.h"
#include "mozilla/TimeStamp.h"
#include "nsTArray.h"
#include <string.h>
namespace mozilla {
namespace gl {
class GLContext;
} // namespace gl
/**
* This class tracks the state of the context for debugging and profiling.
* Each section pushes a new stack entry and must be matched by an end section.
* All nested section must be ended before ending a parent section.
*/
class ContextStateTracker {
public:
ContextStateTracker() {}
private:
bool IsProfiling() { return true; }
protected:
typedef GLuint TimerQueryHandle;
class ContextState {
public:
explicit ContextState(const char* aSectionName)
: mSectionName(aSectionName)
{}
const char* mSectionName;
mozilla::TimeStamp mCpuTimeStart;
mozilla::TimeStamp mCpuTimeEnd;
TimerQueryHandle mStartQueryHandle;
};
ContextState& Top() {
MOZ_ASSERT(mSectionStack.Length());
return mSectionStack[mSectionStack.Length() - 1];
}
nsTArray<ContextState> mCompletedSections;
nsTArray<ContextState> mSectionStack;
};
/*
class ID3D11DeviceContext;
class ContextStateTrackerD3D11 final : public ContextStateTracker {
public:
// TODO Implement me
void PushD3D11Section(ID3D11DeviceContext* aCtxt, const char* aSectionName) {}
void PopD3D11Section(ID3D11DeviceContext* aCtxt, const char* aSectionName) {}
void DestroyD3D11(ID3D11DeviceContext* aCtxt) {}
private:
void Flush();
};
*/
class ContextStateTrackerOGL final : public ContextStateTracker {
typedef mozilla::gl::GLContext GLContext;
public:
void PushOGLSection(GLContext* aGL, const char* aSectionName);
void PopOGLSection(GLContext* aGL, const char* aSectionName);
void DestroyOGL(GLContext* aGL);
private:
void Flush(GLContext* aGL);
};
} // namespace mozilla
#endif

412
gfx/thebes/D3D11Checks.cpp Normal file
View file

@ -0,0 +1,412 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "D3D11Checks.h"
#include "gfxConfig.h"
#include "GfxDriverInfo.h"
#include "gfxPrefs.h"
#include "gfxWindowsPlatform.h"
#include "mozilla/RefPtr.h"
#include "mozilla/gfx/Logging.h"
#include "mozilla/layers/TextureD3D11.h"
#include "nsIGfxInfo.h"
#include <dxgi.h>
#include <dxgi1_2.h>
#include <d3d10_1.h>
#include <d3d11.h>
namespace mozilla {
namespace gfx {
using namespace mozilla::widget;
using mozilla::layers::AutoTextureLock;
/* static */ bool
D3D11Checks::DoesRenderTargetViewNeedRecreating(ID3D11Device *aDevice)
{
bool result = false;
// CreateTexture2D is known to crash on lower feature levels, see bugs
// 1170211 and 1089413.
if (aDevice->GetFeatureLevel() < D3D_FEATURE_LEVEL_10_0) {
return true;
}
RefPtr<ID3D11DeviceContext> deviceContext;
aDevice->GetImmediateContext(getter_AddRefs(deviceContext));
int backbufferWidth = 32; int backbufferHeight = 32;
RefPtr<ID3D11Texture2D> offscreenTexture;
RefPtr<IDXGIKeyedMutex> keyedMutex;
D3D11_TEXTURE2D_DESC offscreenTextureDesc = { 0 };
offscreenTextureDesc.Width = backbufferWidth;
offscreenTextureDesc.Height = backbufferHeight;
offscreenTextureDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
offscreenTextureDesc.MipLevels = 0;
offscreenTextureDesc.ArraySize = 1;
offscreenTextureDesc.SampleDesc.Count = 1;
offscreenTextureDesc.SampleDesc.Quality = 0;
offscreenTextureDesc.Usage = D3D11_USAGE_DEFAULT;
offscreenTextureDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
offscreenTextureDesc.CPUAccessFlags = 0;
offscreenTextureDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
HRESULT hr = aDevice->CreateTexture2D(&offscreenTextureDesc, NULL, getter_AddRefs(offscreenTexture));
if (FAILED(hr)) {
gfxCriticalNote << "DoesRecreatingCreateTexture2DFail";
return false;
}
hr = offscreenTexture->QueryInterface(__uuidof(IDXGIKeyedMutex), (void**)getter_AddRefs(keyedMutex));
if (FAILED(hr)) {
gfxCriticalNote << "DoesRecreatingKeyedMutexFailed";
return false;
}
D3D11_RENDER_TARGET_VIEW_DESC offscreenRTVDesc;
offscreenRTVDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
offscreenRTVDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
offscreenRTVDesc.Texture2D.MipSlice = 0;
RefPtr<ID3D11RenderTargetView> offscreenRTView;
hr = aDevice->CreateRenderTargetView(offscreenTexture, &offscreenRTVDesc, getter_AddRefs(offscreenRTView));
if (FAILED(hr)) {
gfxCriticalNote << "DoesRecreatingCreateRenderTargetViewFailed";
return false;
}
{
// Acquire and clear
HRESULT hr;
AutoTextureLock lock(keyedMutex, hr, INFINITE);
FLOAT color1[4] = { 1, 1, 0.5, 1 };
deviceContext->ClearRenderTargetView(offscreenRTView, color1);
}
{
HRESULT hr;
AutoTextureLock lock(keyedMutex, hr, INFINITE);
FLOAT color2[4] = { 1, 1, 0, 1 };
deviceContext->ClearRenderTargetView(offscreenRTView, color2);
D3D11_TEXTURE2D_DESC desc;
offscreenTexture->GetDesc(&desc);
desc.Usage = D3D11_USAGE_STAGING;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
desc.MiscFlags = 0;
desc.BindFlags = 0;
RefPtr<ID3D11Texture2D> cpuTexture;
hr = aDevice->CreateTexture2D(&desc, NULL, getter_AddRefs(cpuTexture));
if (FAILED(hr)) {
gfxCriticalNote << "DoesRecreatingCreateCPUTextureFailed";
return false;
}
deviceContext->CopyResource(cpuTexture, offscreenTexture);
D3D11_MAPPED_SUBRESOURCE mapped;
hr = deviceContext->Map(cpuTexture, 0, D3D11_MAP_READ, 0, &mapped);
if (FAILED(hr)) {
gfxCriticalNote << "DoesRecreatingMapFailed " << hexa(hr);
return false;
}
int resultColor = *(int*)mapped.pData;
deviceContext->Unmap(cpuTexture, 0);
cpuTexture = nullptr;
// XXX on some drivers resultColor will not have changed to
// match the clear
if (resultColor != 0xffffff00) {
gfxCriticalNote << "RenderTargetViewNeedsRecreating";
result = true;
}
}
return result;
}
/* static */ bool
D3D11Checks::DoesDeviceWork()
{
static bool checked = false;
static bool result = false;
if (checked)
return result;
checked = true;
if (gfxPrefs::Direct2DForceEnabled() ||
gfxConfig::IsForcedOnByUser(Feature::HW_COMPOSITING))
{
result = true;
return true;
}
if (GetModuleHandleW(L"igd10umd32.dll")) {
const wchar_t* checkModules[] = {L"dlumd32.dll",
L"dlumd11.dll",
L"dlumd10.dll"};
for (int i=0; i<PR_ARRAY_SIZE(checkModules); i+=1) {
if (GetModuleHandleW(checkModules[i])) {
nsString displayLinkModuleVersionString;
gfxWindowsPlatform::GetDLLVersion(checkModules[i],
displayLinkModuleVersionString);
uint64_t displayLinkModuleVersion;
if (!ParseDriverVersion(displayLinkModuleVersionString,
&displayLinkModuleVersion)) {
gfxCriticalError() << "DisplayLink: could not parse version "
<< checkModules[i];
return false;
}
if (displayLinkModuleVersion <= V(8,6,1,36484)) {
gfxCriticalError(CriticalLog::DefaultOptions(false)) << "DisplayLink: too old version " << displayLinkModuleVersionString.get();
return false;
}
}
}
}
result = true;
return true;
}
static bool
TryCreateTexture2D(ID3D11Device *device,
D3D11_TEXTURE2D_DESC* desc,
D3D11_SUBRESOURCE_DATA* data,
RefPtr<ID3D11Texture2D>& texture)
{
// Older Intel driver version (see bug 1221348 for version #s) crash when
// creating a texture with shared keyed mutex and data.
MOZ_SEH_TRY {
return !FAILED(device->CreateTexture2D(desc, data, getter_AddRefs(texture)));
} MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
// For now we want to aggregrate all the crash signature to a known crash.
gfxDevCrash(LogReason::TextureCreation) << "Crash creating texture. See bug 1221348.";
return false;
}
}
// See bug 1083071. On some drivers, Direct3D 11 CreateShaderResourceView fails
// with E_OUTOFMEMORY.
static bool
DoesTextureSharingWorkInternal(ID3D11Device *device, DXGI_FORMAT format, UINT bindflags)
{
// CreateTexture2D is known to crash on lower feature levels, see bugs
// 1170211 and 1089413.
if (device->GetFeatureLevel() < D3D_FEATURE_LEVEL_10_0) {
return false;
}
if (gfxPrefs::Direct2DForceEnabled() ||
gfxConfig::IsForcedOnByUser(Feature::HW_COMPOSITING))
{
return true;
}
if (GetModuleHandleW(L"atidxx32.dll")) {
nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
if (gfxInfo) {
nsString vendorID, vendorID2;
gfxInfo->GetAdapterVendorID(vendorID);
gfxInfo->GetAdapterVendorID2(vendorID2);
if (vendorID.EqualsLiteral("0x8086") && vendorID2.IsEmpty()) {
if (!gfxPrefs::LayersAMDSwitchableGfxEnabled()) {
return false;
}
gfxCriticalError(CriticalLog::DefaultOptions(false)) << "PossiblyBrokenSurfaceSharing_UnexpectedAMDGPU";
}
}
}
RefPtr<ID3D11Texture2D> texture;
D3D11_TEXTURE2D_DESC desc;
const int texture_size = 32;
desc.Width = texture_size;
desc.Height = texture_size;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = format;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.CPUAccessFlags = 0;
desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
desc.BindFlags = bindflags;
uint32_t color[texture_size * texture_size];
for (size_t i = 0; i < sizeof(color)/sizeof(color[0]); i++) {
color[i] = 0xff00ffff;
}
// XXX If we pass the data directly at texture creation time we
// get a crash on Intel 8.5.10.[18xx-1994] drivers.
// We can work around this issue by doing UpdateSubresource.
if (!TryCreateTexture2D(device, &desc, nullptr, texture)) {
gfxCriticalNote << "DoesD3D11TextureSharingWork_TryCreateTextureFailure";
return false;
}
RefPtr<IDXGIKeyedMutex> sourceSharedMutex;
texture->QueryInterface(__uuidof(IDXGIKeyedMutex), (void**)getter_AddRefs(sourceSharedMutex));
if (FAILED(sourceSharedMutex->AcquireSync(0, 30*1000))) {
gfxCriticalError() << "DoesD3D11TextureSharingWork_SourceMutexTimeout";
// only wait for 30 seconds
return false;
}
RefPtr<ID3D11DeviceContext> deviceContext;
device->GetImmediateContext(getter_AddRefs(deviceContext));
int stride = texture_size * 4;
deviceContext->UpdateSubresource(texture, 0, nullptr, color, stride, stride * texture_size);
if (FAILED(sourceSharedMutex->ReleaseSync(0))) {
gfxCriticalError() << "DoesD3D11TextureSharingWork_SourceReleaseSyncTimeout";
return false;
}
HANDLE shareHandle;
RefPtr<IDXGIResource> otherResource;
if (FAILED(texture->QueryInterface(__uuidof(IDXGIResource),
getter_AddRefs(otherResource))))
{
gfxCriticalError() << "DoesD3D11TextureSharingWork_GetResourceFailure";
return false;
}
if (FAILED(otherResource->GetSharedHandle(&shareHandle))) {
gfxCriticalError() << "DoesD3D11TextureSharingWork_GetSharedTextureFailure";
return false;
}
RefPtr<ID3D11Resource> sharedResource;
RefPtr<ID3D11Texture2D> sharedTexture;
if (FAILED(device->OpenSharedResource(shareHandle, __uuidof(ID3D11Resource),
getter_AddRefs(sharedResource))))
{
gfxCriticalError(CriticalLog::DefaultOptions(false)) << "OpenSharedResource failed for format " << format;
return false;
}
if (FAILED(sharedResource->QueryInterface(__uuidof(ID3D11Texture2D),
getter_AddRefs(sharedTexture))))
{
gfxCriticalError() << "DoesD3D11TextureSharingWork_GetSharedTextureFailure";
return false;
}
// create a staging texture for readback
RefPtr<ID3D11Texture2D> cpuTexture;
desc.Usage = D3D11_USAGE_STAGING;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
desc.MiscFlags = 0;
desc.BindFlags = 0;
if (FAILED(device->CreateTexture2D(&desc, nullptr, getter_AddRefs(cpuTexture)))) {
gfxCriticalError() << "DoesD3D11TextureSharingWork_CreateTextureFailure";
return false;
}
RefPtr<IDXGIKeyedMutex> sharedMutex;
sharedResource->QueryInterface(__uuidof(IDXGIKeyedMutex), (void**)getter_AddRefs(sharedMutex));
{
HRESULT hr;
AutoTextureLock lock(sharedMutex, hr, 30*1000);
if (FAILED(hr)) {
gfxCriticalError() << "DoesD3D11TextureSharingWork_AcquireSyncTimeout";
// only wait for 30 seconds
return false;
}
// Copy to the cpu texture so that we can readback
deviceContext->CopyResource(cpuTexture, sharedTexture);
// We only need to hold on to the mutex during the copy.
sharedMutex->ReleaseSync(0);
}
D3D11_MAPPED_SUBRESOURCE mapped;
uint32_t resultColor = 0;
if (SUCCEEDED(deviceContext->Map(cpuTexture, 0, D3D11_MAP_READ, 0, &mapped))) {
// read the texture
resultColor = *(uint32_t*)mapped.pData;
deviceContext->Unmap(cpuTexture, 0);
} else {
gfxCriticalError() << "DoesD3D11TextureSharingWork_MapFailed";
return false;
}
// check that the color we put in is the color we get out
if (resultColor != color[0]) {
// Shared surfaces seem to be broken on dual AMD & Intel HW when using the
// AMD GPU
gfxCriticalNote << "DoesD3D11TextureSharingWork_ColorMismatch";
return false;
}
RefPtr<ID3D11ShaderResourceView> sharedView;
// This if(FAILED()) is the one that actually fails on systems affected by bug 1083071.
if (FAILED(device->CreateShaderResourceView(sharedTexture, NULL, getter_AddRefs(sharedView)))) {
gfxCriticalNote << "CreateShaderResourceView failed for format" << format;
return false;
}
return true;
}
/* static */ bool
D3D11Checks::DoesTextureSharingWork(ID3D11Device *device)
{
return DoesTextureSharingWorkInternal(device, DXGI_FORMAT_B8G8R8A8_UNORM, D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE);
}
/* static */ bool
D3D11Checks::DoesAlphaTextureSharingWork(ID3D11Device *device)
{
return DoesTextureSharingWorkInternal(device, DXGI_FORMAT_R8_UNORM, D3D11_BIND_SHADER_RESOURCE);
}
/* static */ bool
D3D11Checks::GetDxgiDesc(ID3D11Device* device, DXGI_ADAPTER_DESC* out)
{
RefPtr<IDXGIDevice> dxgiDevice;
HRESULT hr = device->QueryInterface(__uuidof(IDXGIDevice), getter_AddRefs(dxgiDevice));
if (FAILED(hr)) {
return false;
}
RefPtr<IDXGIAdapter> dxgiAdapter;
if (FAILED(dxgiDevice->GetAdapter(getter_AddRefs(dxgiAdapter)))) {
return false;
}
return SUCCEEDED(dxgiAdapter->GetDesc(out));
}
/* static */ void
D3D11Checks::WarnOnAdapterMismatch(ID3D11Device *device)
{
DXGI_ADAPTER_DESC desc;
PodZero(&desc);
GetDxgiDesc(device, &desc);
nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
nsString vendorID;
gfxInfo->GetAdapterVendorID(vendorID);
nsresult ec;
int32_t vendor = vendorID.ToInteger(&ec, 16);
if (vendor != desc.VendorId) {
gfxCriticalNote << "VendorIDMismatch V " << hexa(vendor) << " " << hexa(desc.VendorId);
}
}
/* static */ bool
D3D11Checks::DoesRemotePresentWork(IDXGIAdapter* adapter)
{
// Remote presentation was added in DXGI 1.2, for Windows 8 and the Platform Update to Windows 7.
RefPtr<IDXGIAdapter2> check;
HRESULT hr = adapter->QueryInterface(__uuidof(IDXGIAdapter2), getter_AddRefs(check));
return SUCCEEDED(hr) && check;
}
} // namespace gfx
} // namespace mozilla

30
gfx/thebes/D3D11Checks.h Normal file
View file

@ -0,0 +1,30 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_gfx_thebes_D3D11Checks_h
#define mozilla_gfx_thebes_D3D11Checks_h
struct ID3D11Device;
struct IDXGIAdapter;
struct DXGI_ADAPTER_DESC;
namespace mozilla {
namespace gfx {
struct D3D11Checks
{
static bool DoesRenderTargetViewNeedRecreating(ID3D11Device* aDevice);
static bool DoesDeviceWork();
static bool DoesTextureSharingWork(ID3D11Device *device);
static bool DoesAlphaTextureSharingWork(ID3D11Device *device);
static void WarnOnAdapterMismatch(ID3D11Device* device);
static bool GetDxgiDesc(ID3D11Device* device, DXGI_ADAPTER_DESC* out);
static bool DoesRemotePresentWork(IDXGIAdapter* adapter);
};
} // namespace gfx
} // namespace mozilla
#endif // mozilla_gfx_thebes_D3D11Checks_h

View file

@ -0,0 +1,878 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DeviceManagerDx.h"
#include "D3D11Checks.h"
#include "gfxConfig.h"
#include "GfxDriverInfo.h"
#include "gfxPrefs.h"
#include "gfxWindowsPlatform.h"
#include "mozilla/D3DMessageUtils.h"
#include "mozilla/Telemetry.h"
#include "mozilla/WindowsVersion.h"
#include "mozilla/gfx/GraphicsMessages.h"
#include "mozilla/gfx/Logging.h"
#include "mozilla/layers/CompositorThread.h"
#include "nsIGfxInfo.h"
#include <d3d11.h>
#include <ddraw.h>
namespace mozilla {
namespace gfx {
using namespace mozilla::widget;
StaticAutoPtr<DeviceManagerDx> DeviceManagerDx::sInstance;
// We don't have access to the D3D11CreateDevice type in gfxWindowsPlatform.h,
// since it doesn't include d3d11.h, so we use a static here. It should only
// be used within InitializeD3D11.
decltype(D3D11CreateDevice)* sD3D11CreateDeviceFn = nullptr;
// We don't have access to the DirectDrawCreateEx type in gfxWindowsPlatform.h,
// since it doesn't include ddraw.h, so we use a static here. It should only
// be used within InitializeDirectDrawConfig.
decltype(DirectDrawCreateEx)* sDirectDrawCreateExFn = nullptr;
/* static */ void
DeviceManagerDx::Init()
{
sInstance = new DeviceManagerDx();
}
/* static */ void
DeviceManagerDx::Shutdown()
{
sInstance = nullptr;
}
DeviceManagerDx::DeviceManagerDx()
: mDeviceLock("gfxWindowsPlatform.mDeviceLock"),
mCompositorDeviceSupportsVideo(false)
{
// Set up the D3D11 feature levels we can ask for.
if (IsWin8OrLater()) {
mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_11_1);
}
mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_11_0);
mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_10_1);
mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_10_0);
}
bool
DeviceManagerDx::LoadD3D11()
{
FeatureState& d3d11 = gfxConfig::GetFeature(Feature::D3D11_COMPOSITING);
MOZ_ASSERT(d3d11.IsEnabled());
if (sD3D11CreateDeviceFn) {
return true;
}
nsModuleHandle module(LoadLibrarySystem32(L"d3d11.dll"));
if (!module) {
d3d11.SetFailed(FeatureStatus::Unavailable, "Direct3D11 not available on this computer",
NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_LIB"));
return false;
}
sD3D11CreateDeviceFn =
(decltype(D3D11CreateDevice)*)GetProcAddress(module, "D3D11CreateDevice");
if (!sD3D11CreateDeviceFn) {
// We should just be on Windows Vista or XP in this case.
d3d11.SetFailed(FeatureStatus::Unavailable, "Direct3D11 not available on this computer",
NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_FUNCPTR"));
return false;
}
mD3D11Module.steal(module);
return true;
}
void
DeviceManagerDx::ReleaseD3D11()
{
MOZ_ASSERT(!mCompositorDevice);
MOZ_ASSERT(!mContentDevice);
MOZ_ASSERT(!mDecoderDevice);
mD3D11Module.reset();
sD3D11CreateDeviceFn = nullptr;
}
static inline bool
ProcessOwnsCompositor()
{
return XRE_GetProcessType() == GeckoProcessType_GPU ||
(XRE_IsParentProcess() && !gfxConfig::IsEnabled(Feature::GPU_PROCESS));
}
bool
DeviceManagerDx::CreateCompositorDevices()
{
MOZ_ASSERT(ProcessOwnsCompositor());
FeatureState& d3d11 = gfxConfig::GetFeature(Feature::D3D11_COMPOSITING);
MOZ_ASSERT(d3d11.IsEnabled());
if (!LoadD3D11()) {
return false;
}
CreateCompositorDevice(d3d11);
if (!d3d11.IsEnabled()) {
MOZ_ASSERT(!mCompositorDevice);
ReleaseD3D11();
return false;
}
// We leak these everywhere and we need them our entire runtime anyway, let's
// leak it here as well. We keep the pointer to sD3D11CreateDeviceFn around
// as well for D2D1 and device resets.
mD3D11Module.disown();
MOZ_ASSERT(mCompositorDevice);
return d3d11.IsEnabled();
}
void
DeviceManagerDx::ImportDeviceInfo(const D3D11DeviceStatus& aDeviceStatus)
{
MOZ_ASSERT(!ProcessOwnsCompositor());
mDeviceStatus = Some(aDeviceStatus);
}
void
DeviceManagerDx::ExportDeviceInfo(D3D11DeviceStatus* aOut)
{
// Even though the parent process might not own the compositor, we still
// populate DeviceManagerDx with device statistics (for simplicity).
// That means it still gets queried for compositor information.
MOZ_ASSERT(XRE_IsParentProcess() || XRE_GetProcessType() == GeckoProcessType_GPU);
if (mDeviceStatus) {
*aOut = mDeviceStatus.value();
}
}
void
DeviceManagerDx::CreateContentDevices()
{
MOZ_ASSERT(gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING));
if (!LoadD3D11()) {
return;
}
// We should have been assigned a DeviceStatus from the parent process,
// GPU process, or the same process if using in-process compositing.
MOZ_ASSERT(mDeviceStatus);
if (CreateContentDevice() == FeatureStatus::CrashedInHandler) {
DisableD3D11AfterCrash();
}
}
IDXGIAdapter1*
DeviceManagerDx::GetDXGIAdapter()
{
if (mAdapter) {
return mAdapter;
}
nsModuleHandle dxgiModule(LoadLibrarySystem32(L"dxgi.dll"));
decltype(CreateDXGIFactory1)* createDXGIFactory1 = (decltype(CreateDXGIFactory1)*)
GetProcAddress(dxgiModule, "CreateDXGIFactory1");
if (!createDXGIFactory1) {
return nullptr;
}
// Try to use a DXGI 1.1 adapter in order to share resources
// across processes.
RefPtr<IDXGIFactory1> factory1;
HRESULT hr = createDXGIFactory1(__uuidof(IDXGIFactory1),
getter_AddRefs(factory1));
if (FAILED(hr) || !factory1) {
// This seems to happen with some people running the iZ3D driver.
// They won't get acceleration.
return nullptr;
}
if (!mDeviceStatus) {
// If we haven't created a device yet, and have no existing device status,
// then this must be the compositor device. Pick the first adapter we can.
if (FAILED(factory1->EnumAdapters1(0, getter_AddRefs(mAdapter)))) {
return nullptr;
}
} else {
// In the UI and GPU process, we clear mDeviceStatus on device reset, so we
// should never reach here. Furthermore, the UI process does not create
// devices when using a GPU process.
//
// So, this should only ever get called on the content process.
MOZ_ASSERT(XRE_IsContentProcess());
// In the child process, we search for the adapter that matches the parent
// process. The first adapter can be mismatched on dual-GPU systems.
for (UINT index = 0; ; index++) {
RefPtr<IDXGIAdapter1> adapter;
if (FAILED(factory1->EnumAdapters1(index, getter_AddRefs(adapter)))) {
break;
}
const DxgiAdapterDesc& preferred = mDeviceStatus->adapter();
DXGI_ADAPTER_DESC desc;
if (SUCCEEDED(adapter->GetDesc(&desc)) &&
desc.AdapterLuid.HighPart == preferred.AdapterLuid.HighPart &&
desc.AdapterLuid.LowPart == preferred.AdapterLuid.LowPart &&
desc.VendorId == preferred.VendorId &&
desc.DeviceId == preferred.DeviceId)
{
mAdapter = adapter.forget();
break;
}
}
}
if (!mAdapter) {
return nullptr;
}
// We leak this module everywhere, we might as well do so here as well.
dxgiModule.disown();
return mAdapter;
}
bool
DeviceManagerDx::CreateCompositorDeviceHelper(
FeatureState& aD3d11, IDXGIAdapter1* aAdapter, bool aAttemptVideoSupport, RefPtr<ID3D11Device>& aOutDevice)
{
// Check if a failure was injected for testing.
if (gfxPrefs::DeviceFailForTesting()) {
aD3d11.SetFailed(FeatureStatus::Failed, "Direct3D11 device failure simulated by preference",
NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_SIM"));
return false;
}
// Use D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS
// to prevent bug 1092260. IE 11 also uses this flag.
UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS;
if (aAttemptVideoSupport) {
flags |= D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
}
HRESULT hr;
RefPtr<ID3D11Device> device;
if (!CreateDevice(aAdapter, D3D_DRIVER_TYPE_UNKNOWN, flags, hr, device)) {
if (!aAttemptVideoSupport) {
gfxCriticalError() << "Crash during D3D11 device creation";
aD3d11.SetFailed(FeatureStatus::CrashedInHandler, "Crashed trying to acquire a D3D11 device",
NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_DEVICE1"));
}
return false;
}
if (FAILED(hr) || !device) {
if (!aAttemptVideoSupport) {
aD3d11.SetFailed(FeatureStatus::Failed, "Failed to acquire a D3D11 device",
NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_DEVICE2"));
}
return false;
}
if (!D3D11Checks::DoesDeviceWork()) {
if (!aAttemptVideoSupport) {
aD3d11.SetFailed(FeatureStatus::Broken, "Direct3D11 device was determined to be broken",
NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_BROKEN"));
}
return false;
}
aOutDevice = device;
return true;
}
void
DeviceManagerDx::CreateCompositorDevice(FeatureState& d3d11)
{
if (gfxPrefs::LayersD3D11ForceWARP()) {
CreateWARPCompositorDevice();
return;
}
RefPtr<IDXGIAdapter1> adapter = GetDXGIAdapter();
if (!adapter) {
d3d11.SetFailed(FeatureStatus::Unavailable, "Failed to acquire a DXGI adapter",
NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_DXGI"));
return;
}
if (XRE_IsGPUProcess() && !D3D11Checks::DoesRemotePresentWork(adapter)) {
d3d11.SetFailed(
FeatureStatus::Unavailable,
"DXGI does not support out-of-process presentation",
NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_REMOTE_PRESENT"));
return;
}
RefPtr<ID3D11Device> device;
if (!CreateCompositorDeviceHelper(d3d11, adapter, true, device)) {
// Try again without video support and record that it failed.
mCompositorDeviceSupportsVideo = false;
if (!CreateCompositorDeviceHelper(d3d11, adapter, false, device)) {
return;
}
} else {
mCompositorDeviceSupportsVideo = true;
}
// Only test this when not using WARP since it can fail and cause
// GetDeviceRemovedReason to return weird values.
bool textureSharingWorks = D3D11Checks::DoesTextureSharingWork(device);
DXGI_ADAPTER_DESC desc;
PodZero(&desc);
adapter->GetDesc(&desc);
if (!textureSharingWorks) {
gfxConfig::SetFailed(Feature::D3D11_HW_ANGLE,
FeatureStatus::Broken,
"Texture sharing doesn't work");
}
if (D3D11Checks::DoesRenderTargetViewNeedRecreating(device)) {
gfxConfig::SetFailed(Feature::D3D11_HW_ANGLE,
FeatureStatus::Broken,
"RenderTargetViews need recreating");
}
if (XRE_IsParentProcess()) {
// It seems like this may only happen when we're using the NVIDIA gpu
D3D11Checks::WarnOnAdapterMismatch(device);
}
int featureLevel = device->GetFeatureLevel();
{
MutexAutoLock lock(mDeviceLock);
mCompositorDevice = device;
mDeviceStatus = Some(D3D11DeviceStatus(
false,
textureSharingWorks,
featureLevel,
DxgiAdapterDesc::From(desc)));
}
mCompositorDevice->SetExceptionMode(0);
}
bool
DeviceManagerDx::CreateDevice(IDXGIAdapter* aAdapter,
D3D_DRIVER_TYPE aDriverType,
UINT aFlags,
HRESULT& aResOut,
RefPtr<ID3D11Device>& aOutDevice)
{
MOZ_SEH_TRY {
aResOut = sD3D11CreateDeviceFn(
aAdapter, aDriverType, nullptr,
aFlags,
mFeatureLevels.Elements(), mFeatureLevels.Length(),
D3D11_SDK_VERSION, getter_AddRefs(aOutDevice), nullptr, nullptr);
} MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
return false;
}
return true;
}
void
DeviceManagerDx::CreateWARPCompositorDevice()
{
ScopedGfxFeatureReporter reporterWARP("D3D11-WARP", gfxPrefs::LayersD3D11ForceWARP());
FeatureState& d3d11 = gfxConfig::GetFeature(Feature::D3D11_COMPOSITING);
HRESULT hr;
RefPtr<ID3D11Device> device;
// Use D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS
// to prevent bug 1092260. IE 11 also uses this flag.
UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
if (!CreateDevice(nullptr, D3D_DRIVER_TYPE_WARP, flags, hr, device)) {
gfxCriticalError() << "Exception occurred initializing WARP D3D11 device!";
d3d11.SetFailed(FeatureStatus::CrashedInHandler, "Crashed creating a D3D11 WARP device",
NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_WARP_DEVICE"));
}
if (FAILED(hr) || !device) {
// This should always succeed... in theory.
gfxCriticalError() << "Failed to initialize WARP D3D11 device! " << hexa(hr);
d3d11.SetFailed(FeatureStatus::Failed, "Failed to create a D3D11 WARP device",
NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_WARP_DEVICE2"));
return;
}
// Only test for texture sharing on Windows 8 since it puts the device into
// an unusable state if used on Windows 7
bool textureSharingWorks = false;
if (IsWin8OrLater()) {
textureSharingWorks = D3D11Checks::DoesTextureSharingWork(device);
}
DxgiAdapterDesc nullAdapter;
PodZero(&nullAdapter);
int featureLevel = device->GetFeatureLevel();
{
MutexAutoLock lock(mDeviceLock);
mCompositorDevice = device;
mDeviceStatus = Some(D3D11DeviceStatus(
true,
textureSharingWorks,
featureLevel,
nullAdapter));
}
mCompositorDevice->SetExceptionMode(0);
reporterWARP.SetSuccessful();
}
FeatureStatus
DeviceManagerDx::CreateContentDevice()
{
RefPtr<IDXGIAdapter1> adapter;
if (!mDeviceStatus->isWARP()) {
adapter = GetDXGIAdapter();
if (!adapter) {
gfxCriticalNote << "Could not get a DXGI adapter";
return FeatureStatus::Unavailable;
}
}
HRESULT hr;
RefPtr<ID3D11Device> device;
UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
D3D_DRIVER_TYPE type = mDeviceStatus->isWARP()
? D3D_DRIVER_TYPE_WARP
: D3D_DRIVER_TYPE_UNKNOWN;
if (!CreateDevice(adapter, type, flags, hr, device)) {
gfxCriticalNote << "Recovered from crash while creating a D3D11 content device";
gfxWindowsPlatform::RecordContentDeviceFailure(TelemetryDeviceCode::Content);
return FeatureStatus::CrashedInHandler;
}
if (FAILED(hr) || !device) {
gfxCriticalNote << "Failed to create a D3D11 content device: " << hexa(hr);
gfxWindowsPlatform::RecordContentDeviceFailure(TelemetryDeviceCode::Content);
return FeatureStatus::Failed;
}
// InitializeD2D() will abort early if the compositor device did not support
// texture sharing. If we're in the content process, we can't rely on the
// parent device alone: some systems have dual GPUs that are capable of
// binding the parent and child processes to different GPUs. As a safety net,
// we re-check texture sharing against the newly created D3D11 content device.
// If it fails, we won't use Direct2D.
if (XRE_IsContentProcess()) {
if (!D3D11Checks::DoesTextureSharingWork(device)) {
return FeatureStatus::Failed;
}
DebugOnly<bool> ok = ContentAdapterIsParentAdapter(device);
MOZ_ASSERT(ok);
}
{
MutexAutoLock lock(mDeviceLock);
mContentDevice = device;
}
mContentDevice->SetExceptionMode(0);
RefPtr<ID3D10Multithread> multi;
hr = mContentDevice->QueryInterface(__uuidof(ID3D10Multithread), getter_AddRefs(multi));
if (SUCCEEDED(hr) && multi) {
multi->SetMultithreadProtected(TRUE);
}
return FeatureStatus::Available;
}
RefPtr<ID3D11Device>
DeviceManagerDx::CreateDecoderDevice()
{
bool isAMD = false;
{
MutexAutoLock lock(mDeviceLock);
if (!mDeviceStatus) {
return nullptr;
}
isAMD = mDeviceStatus->adapter().VendorId == 0x1002;
}
bool reuseDevice = false;
if (gfxPrefs::Direct3D11ReuseDecoderDevice() < 0) {
// Use the default logic, which is to allow reuse of devices on AMD, but create
// separate devices everywhere else.
if (isAMD) {
reuseDevice = true;
}
} else if (gfxPrefs::Direct3D11ReuseDecoderDevice() > 0) {
reuseDevice = true;
}
if (reuseDevice) {
if (mCompositorDevice && mCompositorDeviceSupportsVideo && !mDecoderDevice) {
mDecoderDevice = mCompositorDevice;
RefPtr<ID3D10Multithread> multi;
mDecoderDevice->QueryInterface(__uuidof(ID3D10Multithread), getter_AddRefs(multi));
if (multi) {
multi->SetMultithreadProtected(TRUE);
}
}
if (mDecoderDevice) {
RefPtr<ID3D11Device> dev = mDecoderDevice;
return dev.forget();
}
}
if (!sD3D11CreateDeviceFn) {
// We should just be on Windows Vista or XP in this case.
return nullptr;
}
RefPtr<IDXGIAdapter1> adapter = GetDXGIAdapter();
if (!adapter) {
return nullptr;
}
HRESULT hr;
RefPtr<ID3D11Device> device;
UINT flags = D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS |
D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
if (!CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, flags, hr, device)) {
return nullptr;
}
if (FAILED(hr) || !device || !D3D11Checks::DoesDeviceWork()) {
return nullptr;
}
RefPtr<ID3D10Multithread> multi;
device->QueryInterface(__uuidof(ID3D10Multithread), getter_AddRefs(multi));
multi->SetMultithreadProtected(TRUE);
if (reuseDevice) {
mDecoderDevice = device;
}
return device;
}
void
DeviceManagerDx::ResetDevices()
{
MutexAutoLock lock(mDeviceLock);
mAdapter = nullptr;
mCompositorDevice = nullptr;
mContentDevice = nullptr;
mDeviceStatus = Nothing();
mDeviceResetReason = Nothing();
Factory::SetDirect3D11Device(nullptr);
}
bool
DeviceManagerDx::MaybeResetAndReacquireDevices()
{
DeviceResetReason resetReason;
if (!HasDeviceReset(&resetReason)) {
return false;
}
if (resetReason != DeviceResetReason::FORCED_RESET) {
Telemetry::Accumulate(Telemetry::DEVICE_RESET_REASON, uint32_t(resetReason));
}
bool createCompositorDevice = !!mCompositorDevice;
bool createContentDevice = !!mContentDevice;
ResetDevices();
if (createCompositorDevice && !CreateCompositorDevices()) {
// Just stop, don't try anything more
return true;
}
if (createContentDevice) {
CreateContentDevices();
}
return true;
}
bool
DeviceManagerDx::ContentAdapterIsParentAdapter(ID3D11Device* device)
{
DXGI_ADAPTER_DESC desc;
if (!D3D11Checks::GetDxgiDesc(device, &desc)) {
gfxCriticalNote << "Could not query device DXGI adapter info";
return false;
}
const DxgiAdapterDesc& preferred = mDeviceStatus->adapter();
if (desc.VendorId != preferred.VendorId ||
desc.DeviceId != preferred.DeviceId ||
desc.SubSysId != preferred.SubSysId ||
desc.AdapterLuid.HighPart != preferred.AdapterLuid.HighPart ||
desc.AdapterLuid.LowPart != preferred.AdapterLuid.LowPart)
{
gfxCriticalNote <<
"VendorIDMismatch P " <<
hexa(preferred.VendorId) << " " <<
hexa(desc.VendorId);
return false;
}
return true;
}
static DeviceResetReason HResultToResetReason(HRESULT hr)
{
switch (hr) {
case DXGI_ERROR_DEVICE_HUNG:
return DeviceResetReason::HUNG;
case DXGI_ERROR_DEVICE_REMOVED:
return DeviceResetReason::REMOVED;
case DXGI_ERROR_DEVICE_RESET:
return DeviceResetReason::RESET;
case DXGI_ERROR_DRIVER_INTERNAL_ERROR:
return DeviceResetReason::DRIVER_ERROR;
case DXGI_ERROR_INVALID_CALL:
return DeviceResetReason::INVALID_CALL;
case E_OUTOFMEMORY:
return DeviceResetReason::OUT_OF_MEMORY;
default:
MOZ_ASSERT(false);
}
return DeviceResetReason::UNKNOWN;
}
bool
DeviceManagerDx::HasDeviceReset(DeviceResetReason* aOutReason)
{
MutexAutoLock lock(mDeviceLock);
if (mDeviceResetReason) {
if (aOutReason) {
*aOutReason = mDeviceResetReason.value();
}
return true;
}
DeviceResetReason reason;
if (GetAnyDeviceRemovedReason(&reason)) {
mDeviceResetReason = Some(reason);
if (aOutReason) {
*aOutReason = reason;
}
return true;
}
return false;
}
static inline bool
DidDeviceReset(const RefPtr<ID3D11Device>& aDevice, DeviceResetReason* aOutReason)
{
if (!aDevice) {
return false;
}
HRESULT hr = aDevice->GetDeviceRemovedReason();
if (hr == S_OK) {
return false;
}
*aOutReason = HResultToResetReason(hr);
return true;
}
bool
DeviceManagerDx::GetAnyDeviceRemovedReason(DeviceResetReason* aOutReason)
{
// Caller must own the lock, since we access devices directly, and can be
// called from any thread.
mDeviceLock.AssertCurrentThreadOwns();
if (DidDeviceReset(mCompositorDevice, aOutReason) ||
DidDeviceReset(mContentDevice, aOutReason))
{
return true;
}
if (XRE_IsParentProcess() &&
NS_IsMainThread() &&
gfxPrefs::DeviceResetForTesting())
{
gfxPrefs::SetDeviceResetForTesting(0);
*aOutReason = DeviceResetReason::FORCED_RESET;
return true;
}
return false;
}
void
DeviceManagerDx::ForceDeviceReset(ForcedDeviceResetReason aReason)
{
Telemetry::Accumulate(Telemetry::FORCED_DEVICE_RESET_REASON, uint32_t(aReason));
{
MutexAutoLock lock(mDeviceLock);
mDeviceResetReason = Some(DeviceResetReason::FORCED_RESET);
}
}
void
DeviceManagerDx::NotifyD3D9DeviceReset()
{
MutexAutoLock lock(mDeviceLock);
mDeviceResetReason = Some(DeviceResetReason::D3D9_RESET);
}
void
DeviceManagerDx::DisableD3D11AfterCrash()
{
gfxConfig::Disable(Feature::D3D11_COMPOSITING,
FeatureStatus::CrashedInHandler,
"Crashed while acquiring a Direct3D11 device",
NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_CRASH"));
ResetDevices();
}
RefPtr<ID3D11Device>
DeviceManagerDx::GetCompositorDevice()
{
MutexAutoLock lock(mDeviceLock);
return mCompositorDevice;
}
RefPtr<ID3D11Device>
DeviceManagerDx::GetContentDevice()
{
MutexAutoLock lock(mDeviceLock);
return mContentDevice;
}
unsigned
DeviceManagerDx::GetCompositorFeatureLevel() const
{
if (!mDeviceStatus) {
return 0;
}
return mDeviceStatus->featureLevel();
}
bool
DeviceManagerDx::TextureSharingWorks()
{
MutexAutoLock lock(mDeviceLock);
if (!mDeviceStatus) {
return false;
}
return mDeviceStatus->textureSharingWorks();
}
bool
DeviceManagerDx::CanInitializeKeyedMutexTextures()
{
MutexAutoLock lock(mDeviceLock);
if (!mDeviceStatus) {
return false;
}
// Disable this on all Intel devices because of crashes.
// See bug 1292923.
return mDeviceStatus->adapter().VendorId != 0x8086;
}
bool
DeviceManagerDx::CheckRemotePresentSupport()
{
MOZ_ASSERT(XRE_IsParentProcess());
RefPtr<IDXGIAdapter1> adapter = GetDXGIAdapter();
if (!adapter) {
return false;
}
if (!D3D11Checks::DoesRemotePresentWork(adapter)) {
return false;
}
return true;
}
bool
DeviceManagerDx::IsWARP()
{
MutexAutoLock lock(mDeviceLock);
if (!mDeviceStatus) {
return false;
}
return mDeviceStatus->isWARP();
}
void
DeviceManagerDx::InitializeDirectDraw()
{
MOZ_ASSERT(layers::CompositorThreadHolder::IsInCompositorThread());
if (mDirectDraw) {
// Already initialized.
return;
}
FeatureState& ddraw = gfxConfig::GetFeature(Feature::DIRECT_DRAW);
if (!ddraw.IsEnabled()) {
return;
}
// Check if DirectDraw is available on this system.
mDirectDrawDLL.own(LoadLibrarySystem32(L"ddraw.dll"));
if (!mDirectDrawDLL) {
ddraw.SetFailed(FeatureStatus::Unavailable, "DirectDraw not available on this computer",
NS_LITERAL_CSTRING("FEATURE_FAILURE_DDRAW_LIB"));
return;
}
sDirectDrawCreateExFn =
(decltype(DirectDrawCreateEx)*)GetProcAddress(mDirectDrawDLL, "DirectDrawCreateEx");
if (!sDirectDrawCreateExFn) {
ddraw.SetFailed(FeatureStatus::Unavailable, "DirectDraw not available on this computer",
NS_LITERAL_CSTRING("FEATURE_FAILURE_DDRAW_LIB"));
return;
}
HRESULT hr;
MOZ_SEH_TRY {
hr = sDirectDrawCreateExFn(nullptr, getter_AddRefs(mDirectDraw), IID_IDirectDraw7, nullptr);
} MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
ddraw.SetFailed(FeatureStatus::Failed, "Failed to create DirectDraw",
NS_LITERAL_CSTRING("FEATURE_FAILURE_DDRAW_LIB"));
gfxCriticalNote << "DoesCreatingDirectDrawFailed";
return;
}
if (FAILED(hr)) {
ddraw.SetFailed(FeatureStatus::Failed, "Failed to create DirectDraw",
NS_LITERAL_CSTRING("FEATURE_FAILURE_DDRAW_LIB"));
gfxCriticalNote << "DoesCreatingDirectDrawFailed " << hexa(hr);
return;
}
}
IDirectDraw7*
DeviceManagerDx::GetDirectDraw()
{
return mDirectDraw;
}
} // namespace gfx
} // namespace mozilla

View file

@ -0,0 +1,154 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_gfx_thebes_DeviceManagerDx_h
#define mozilla_gfx_thebes_DeviceManagerDx_h
#include "gfxPlatform.h"
#include "gfxTelemetry.h"
#include "mozilla/Maybe.h"
#include "mozilla/Mutex.h"
#include "mozilla/RefPtr.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/gfx/GraphicsMessages.h"
#include "nsTArray.h"
#include "nsWindowsHelpers.h"
#include <windows.h>
#include <objbase.h>
#include <dxgi.h>
// This header is available in the June 2010 SDK and in the Win8 SDK
#include <d3dcommon.h>
// Win 8.0 SDK types we'll need when building using older sdks.
#if !defined(D3D_FEATURE_LEVEL_11_1) // defined in the 8.0 SDK only
#define D3D_FEATURE_LEVEL_11_1 static_cast<D3D_FEATURE_LEVEL>(0xb100)
#define D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION 2048
#define D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION 4096
#endif
struct ID3D11Device;
struct IDirectDraw7;
namespace mozilla {
class ScopedGfxFeatureReporter;
namespace gfx {
class FeatureState;
class DeviceManagerDx final
{
public:
static void Init();
static void Shutdown();
DeviceManagerDx();
static DeviceManagerDx* Get() {
return sInstance;
}
RefPtr<ID3D11Device> GetCompositorDevice();
RefPtr<ID3D11Device> GetContentDevice();
RefPtr<ID3D11Device> CreateDecoderDevice();
IDirectDraw7* GetDirectDraw();
unsigned GetCompositorFeatureLevel() const;
bool TextureSharingWorks();
bool IsWARP();
// Returns true if we can create a texture with
// D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX and also
// upload texture data during the CreateTexture2D
// call. This crashes on some devices, so we might
// need to avoid it.
bool CanInitializeKeyedMutexTextures();
bool CreateCompositorDevices();
void CreateContentDevices();
void ImportDeviceInfo(const D3D11DeviceStatus& aDeviceStatus);
void ExportDeviceInfo(D3D11DeviceStatus* aOut);
void ResetDevices();
void InitializeDirectDraw();
// Reset and reacquire the devices if a reset has happened.
// Returns whether a reset occurred not whether reacquiring
// was successful.
bool MaybeResetAndReacquireDevices();
// Test whether we can acquire a DXGI 1.2-compatible adapter. This should
// only be called on startup before devices are initialized.
bool CheckRemotePresentSupport();
// Device reset helpers.
bool HasDeviceReset(DeviceResetReason* aOutReason = nullptr);
// Note: these set the cached device reset reason, which will be picked up
// on the next frame.
void ForceDeviceReset(ForcedDeviceResetReason aReason);
void NotifyD3D9DeviceReset();
private:
IDXGIAdapter1 *GetDXGIAdapter();
void DisableD3D11AfterCrash();
void CreateCompositorDevice(mozilla::gfx::FeatureState& d3d11);
bool CreateCompositorDeviceHelper(
mozilla::gfx::FeatureState& aD3d11,
IDXGIAdapter1* aAdapter,
bool aAttemptVideoSupport,
RefPtr<ID3D11Device>& aOutDevice);
void CreateWARPCompositorDevice();
mozilla::gfx::FeatureStatus CreateContentDevice();
bool CreateDevice(IDXGIAdapter* aAdapter,
D3D_DRIVER_TYPE aDriverType,
UINT aFlags,
HRESULT& aResOut,
RefPtr<ID3D11Device>& aOutDevice);
bool ContentAdapterIsParentAdapter(ID3D11Device* device);
bool LoadD3D11();
void ReleaseD3D11();
// Call GetDeviceRemovedReason on each device until one returns
// a failure.
bool GetAnyDeviceRemovedReason(DeviceResetReason* aOutReason);
private:
static StaticAutoPtr<DeviceManagerDx> sInstance;
// This is assigned during device creation. Afterwards, it is released if
// devices failed, and "forgotten" if devices succeeded (meaning, we leak
// the ref and unassign the module).
nsModuleHandle mD3D11Module;
mozilla::Mutex mDeviceLock;
nsTArray<D3D_FEATURE_LEVEL> mFeatureLevels;
RefPtr<IDXGIAdapter1> mAdapter;
RefPtr<ID3D11Device> mCompositorDevice;
RefPtr<ID3D11Device> mContentDevice;
RefPtr<ID3D11Device> mDecoderDevice;
bool mCompositorDeviceSupportsVideo;
Maybe<D3D11DeviceStatus> mDeviceStatus;
nsModuleHandle mDirectDrawDLL;
RefPtr<IDirectDraw7> mDirectDraw;
Maybe<DeviceResetReason> mDeviceResetReason;
};
} // namespace gfx
} // namespace mozilla
#endif // mozilla_gfx_thebes_DeviceManagerDx_h

26
gfx/thebes/DrawMode.h Normal file
View file

@ -0,0 +1,26 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef DrawMode_h
#define DrawMode_h
#include "mozilla/TypedEnumBits.h"
// Options for how the text should be drawn
enum class DrawMode : int {
// GLYPH_FILL and GLYPH_STROKE draw into the current context
// and may be used together with bitwise OR.
GLYPH_FILL = 1 << 0,
// Note: using GLYPH_STROKE will destroy the current path.
GLYPH_STROKE = 1 << 1,
// Appends glyphs to the current path. Can NOT be used with
// GLYPH_FILL or GLYPH_STROKE.
GLYPH_PATH = 1 << 2,
// When GLYPH_FILL and GLYPH_STROKE are both set, draws the
// stroke underneath the fill.
GLYPH_STROKE_UNDERNEATH = 1 << 3
};
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(DrawMode)
#endif

159
gfx/thebes/PrintTarget.cpp Normal file
View file

@ -0,0 +1,159 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "PrintTarget.h"
#include "cairo.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/Logging.h"
namespace mozilla {
namespace gfx {
PrintTarget::PrintTarget(cairo_surface_t* aCairoSurface, const IntSize& aSize)
: mCairoSurface(aCairoSurface)
, mSize(aSize)
, mIsFinished(false)
#ifdef DEBUG
, mHasActivePage(false)
#endif
{
#if 0
// aCairoSurface is null when our PrintTargetThebes subclass's ctor calls us.
// Once PrintTargetThebes is removed, enable this assertion.
MOZ_ASSERT(aCairoSurface && !cairo_surface_status(aCairoSurface),
"CreateOrNull factory methods should not call us without a "
"valid cairo_surface_t*");
#endif
// CreateOrNull factory methods hand over ownership of aCairoSurface,
// so we don't call cairo_surface_reference(aSurface) here.
// This code was copied from gfxASurface::Init:
#ifdef MOZ_TREE_CAIRO
if (mCairoSurface &&
cairo_surface_get_content(mCairoSurface) != CAIRO_CONTENT_COLOR) {
cairo_surface_set_subpixel_antialiasing(mCairoSurface,
CAIRO_SUBPIXEL_ANTIALIASING_DISABLED);
}
#endif
}
PrintTarget::~PrintTarget()
{
// null surfaces are allowed here
cairo_surface_destroy(mCairoSurface);
mCairoSurface = nullptr;
}
already_AddRefed<DrawTarget>
PrintTarget::MakeDrawTarget(const IntSize& aSize,
DrawEventRecorder* aRecorder)
{
MOZ_ASSERT(mCairoSurface,
"We shouldn't have been constructed without a cairo surface");
// This should not be called outside of BeginPage()/EndPage() calls since
// some backends can only provide a valid DrawTarget at that time.
MOZ_ASSERT(mHasActivePage, "We can't guarantee a valid DrawTarget");
if (cairo_surface_status(mCairoSurface)) {
return nullptr;
}
// Note than aSize may not be the same as mSize (the size of mCairoSurface).
// See the comments in our header. If the sizes are different a clip will
// be applied to mCairoSurface.
RefPtr<DrawTarget> dt =
Factory::CreateDrawTargetForCairoSurface(mCairoSurface, aSize);
if (!dt || !dt->IsValid()) {
return nullptr;
}
if (aRecorder) {
dt = CreateRecordingDrawTarget(aRecorder, dt);
if (!dt || !dt->IsValid()) {
return nullptr;
}
}
return dt.forget();
}
already_AddRefed<DrawTarget>
PrintTarget::GetReferenceDrawTarget(DrawEventRecorder* aRecorder)
{
if (!mRefDT) {
IntSize size(1, 1);
cairo_surface_t* surface =
cairo_surface_create_similar(mCairoSurface,
cairo_surface_get_content(mCairoSurface),
size.width, size.height);
if (cairo_surface_status(surface)) {
return nullptr;
}
RefPtr<DrawTarget> dt =
Factory::CreateDrawTargetForCairoSurface(surface, size);
// The DT addrefs the surface, so we need drop our own reference to it:
cairo_surface_destroy(surface);
if (!dt || !dt->IsValid()) {
return nullptr;
}
if (aRecorder) {
dt = CreateRecordingDrawTarget(aRecorder, dt);
if (!dt || !dt->IsValid()) {
return nullptr;
}
}
mRefDT = dt.forget();
}
return do_AddRef(mRefDT);
}
already_AddRefed<DrawTarget>
PrintTarget::CreateRecordingDrawTarget(DrawEventRecorder* aRecorder,
DrawTarget* aDrawTarget)
{
MOZ_ASSERT(aRecorder);
MOZ_ASSERT(aDrawTarget);
RefPtr<DrawTarget> dt;
if (aRecorder) {
// It doesn't really matter what we pass as the DrawTarget here.
dt = gfx::Factory::CreateRecordingDrawTarget(aRecorder, aDrawTarget);
}
if (!dt || !dt->IsValid()) {
gfxCriticalNote
<< "Failed to create a recording DrawTarget for PrintTarget";
return nullptr;
}
return dt.forget();
}
void
PrintTarget::Finish()
{
if (mIsFinished) {
return;
}
mIsFinished = true;
// null surfaces are allowed here
cairo_surface_finish(mCairoSurface);
}
} // namespace gfx
} // namespace mozilla

160
gfx/thebes/PrintTarget.h Normal file
View file

@ -0,0 +1,160 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MOZILLA_GFX_PRINTTARGET_H
#define MOZILLA_GFX_PRINTTARGET_H
#include "mozilla/RefPtr.h"
#include "mozilla/gfx/2D.h"
#include "nsISupportsImpl.h"
namespace mozilla {
namespace gfx {
class DrawEventRecorder;
/**
* A class that is used to draw output that is to be sent to a printer or print
* preview.
*
* This class wraps a cairo_surface_t* and provides access to it via a
* DrawTarget. The various checkpointing methods manage the state of the
* platform specific cairo_surface_t*.
*/
class PrintTarget {
public:
NS_INLINE_DECL_REFCOUNTING(PrintTarget);
/// Must be matched 1:1 by an EndPrinting/AbortPrinting call.
virtual nsresult BeginPrinting(const nsAString& aTitle,
const nsAString& aPrintToFileName) {
return NS_OK;
}
virtual nsresult EndPrinting() {
return NS_OK;
}
virtual nsresult AbortPrinting() {
#ifdef DEBUG
mHasActivePage = false;
#endif
return NS_OK;
}
virtual nsresult BeginPage() {
#ifdef DEBUG
MOZ_ASSERT(!mHasActivePage, "Missing EndPage() call");
mHasActivePage = true;
#endif
return NS_OK;
}
virtual nsresult EndPage() {
#ifdef DEBUG
mHasActivePage = false;
#endif
return NS_OK;
}
/**
* Releases the resources used by this PrintTarget. Typically this should be
* called after calling EndPrinting(). Calling this more than once is
* allowed, but subsequent calls are a no-op.
*
* Note that any DrawTarget obtained from this PrintTarget will no longer be
* useful after this method has been called.
*/
virtual void Finish();
/**
* Returns true if to print landscape our consumers must apply a 90 degrees
* rotation to our DrawTarget.
*/
virtual bool RotateNeededForLandscape() const {
return false;
}
const IntSize& GetSize() const {
return mSize;
}
/**
* Makes a DrawTarget to draw the printer output to, or returns null on
* failure.
*
* If aRecorder is passed a recording DrawTarget will be created instead of
* the type of DrawTarget that would normally be returned for a particular
* subclass of this class. This argument is only intended to be used in
* the e10s content process if printing output can't otherwise be transfered
* over to the parent process using the normal DrawTarget type.
*
* NOTE: this should only be called between BeginPage()/EndPage() calls, and
* the returned DrawTarget should not be drawn to after EndPage() has been
* called.
*
* XXX For consistency with the old code this takes a size parameter even
* though we already have the size passed to our subclass's CreateOrNull
* factory methods. The size passed to the factory method comes from
* nsIDeviceContextSpec::MakePrintTarget overrides, whereas the size
* passed to us comes from nsDeviceContext::CreateRenderingContext. In at
* least one case (nsDeviceContextSpecAndroid::MakePrintTarget) these are
* different. At some point we should align the two sources and get rid of
* this method's size parameter.
*
* XXX For consistency with the old code this returns a new DrawTarget for
* each call. Perhaps we can create and cache a DrawTarget in our subclass's
* CreateOrNull factory methods and return that on each call? Currently that
* seems to cause Mochitest failures on Windows though, which coincidentally
* is the only platform where we get passed an aRecorder. Probably the
* issue is that we get called more than once with a different aRecorder, so
* storing one recording DrawTarget for our lifetime doesn't currently work.
*
* XXX Could we pass aRecorder to our subclass's CreateOrNull factory methods?
* We'd need to check that our consumers always pass the same aRecorder for
* our entire lifetime.
*
* XXX Once PrintTargetThebes is removed this can become non-virtual.
*
* XXX In the long run, this class and its sub-classes should be converted to
* use STL classes and mozilla::RefCounted<> so the can be moved to Moz2D.
*
* TODO: Consider adding a SetDPI method that calls
* cairo_surface_set_fallback_resolution.
*/
virtual already_AddRefed<DrawTarget>
MakeDrawTarget(const IntSize& aSize,
DrawEventRecorder* aRecorder = nullptr);
/**
* Returns a reference DrawTarget. Unlike MakeDrawTarget, this method is not
* restricted to being called between BeginPage()/EndPage() calls, and the
* returned DrawTarget it is still valid to use after EndPage() has been
* called.
*/
virtual already_AddRefed<DrawTarget> GetReferenceDrawTarget(DrawEventRecorder* aRecorder);
protected:
// Only created via subclass's constructors
explicit PrintTarget(cairo_surface_t* aCairoSurface, const IntSize& aSize);
// Protected because we're refcounted
virtual ~PrintTarget();
already_AddRefed<DrawTarget>
CreateRecordingDrawTarget(DrawEventRecorder* aRecorder,
DrawTarget* aDrawTarget);
cairo_surface_t* mCairoSurface;
RefPtr<DrawTarget> mRefDT; // reference DT
IntSize mSize;
bool mIsFinished;
#ifdef DEBUG
bool mHasActivePage;
#endif
};
} // namespace gfx
} // namespace mozilla
#endif /* MOZILLA_GFX_PRINTTARGET_H */

View file

@ -0,0 +1,120 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "PrintTargetCG.h"
#include "cairo.h"
#include "cairo-quartz.h"
#include "mozilla/gfx/HelpersCairo.h"
namespace mozilla {
namespace gfx {
PrintTargetCG::PrintTargetCG(cairo_surface_t* aCairoSurface,
const IntSize& aSize)
: PrintTarget(aCairoSurface, aSize)
{
// TODO: Add memory reporting like gfxQuartzSurface.
//RecordMemoryUsed(mSize.height * 4 + sizeof(gfxQuartzSurface));
}
/* static */ already_AddRefed<PrintTargetCG>
PrintTargetCG::CreateOrNull(const IntSize& aSize, gfxImageFormat aFormat)
{
if (!Factory::CheckSurfaceSize(aSize)) {
return nullptr;
}
unsigned int width = static_cast<unsigned int>(aSize.width);
unsigned int height = static_cast<unsigned int>(aSize.height);
cairo_format_t cformat = GfxFormatToCairoFormat(aFormat);
cairo_surface_t* surface =
cairo_quartz_surface_create(cformat, width, height);
if (cairo_surface_status(surface)) {
return nullptr;
}
// The new object takes ownership of our surface reference.
RefPtr<PrintTargetCG> target = new PrintTargetCG(surface, aSize);
return target.forget();
}
/* static */ already_AddRefed<PrintTargetCG>
PrintTargetCG::CreateOrNull(CGContextRef aContext, const IntSize& aSize)
{
if (!Factory::CheckSurfaceSize(aSize)) {
return nullptr;
}
unsigned int width = static_cast<unsigned int>(aSize.width);
unsigned int height = static_cast<unsigned int>(aSize.height);
cairo_surface_t* surface =
cairo_quartz_surface_create_for_cg_context(aContext, width, height);
if (cairo_surface_status(surface)) {
return nullptr;
}
// The new object takes ownership of our surface reference.
RefPtr<PrintTargetCG> target = new PrintTargetCG(surface, aSize);
return target.forget();
}
static size_t
PutBytesNull(void* info, const void* buffer, size_t count)
{
return count;
}
already_AddRefed<DrawTarget>
PrintTargetCG::GetReferenceDrawTarget(DrawEventRecorder* aRecorder)
{
if (!mRefDT) {
const IntSize size(1, 1);
CGDataConsumerCallbacks callbacks = {PutBytesNull, nullptr};
CGDataConsumerRef consumer = CGDataConsumerCreate(nullptr, &callbacks);
CGContextRef pdfContext = CGPDFContextCreate(consumer, nullptr, nullptr);
CGDataConsumerRelease(consumer);
cairo_surface_t* similar =
cairo_quartz_surface_create_for_cg_context(
pdfContext, size.width, size.height);
CGContextRelease(pdfContext);
if (cairo_surface_status(similar)) {
return nullptr;
}
RefPtr<DrawTarget> dt =
Factory::CreateDrawTargetForCairoSurface(similar, size);
// The DT addrefs the surface, so we need drop our own reference to it:
cairo_surface_destroy(similar);
if (!dt || !dt->IsValid()) {
return nullptr;
}
if (aRecorder) {
dt = CreateRecordingDrawTarget(aRecorder, dt);
if (!dt || !dt->IsValid()) {
return nullptr;
}
}
mRefDT = dt.forget();
}
return do_AddRef(mRefDT);
}
} // namespace gfx
} // namespace mozilla

View file

@ -0,0 +1,42 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MOZILLA_GFX_PRINTTARGETCG_H
#define MOZILLA_GFX_PRINTTARGETCG_H
#include <Carbon/Carbon.h>
#include "PrintTarget.h"
namespace mozilla {
namespace gfx {
/**
* CoreGraphics printing target.
*
* Note that a CGContextRef obtained from PMSessionGetCGGraphicsContext is
* valid only for the current page. As a consequence instances of this class
* should only be used to print a single page.
*/
class PrintTargetCG final : public PrintTarget
{
public:
static already_AddRefed<PrintTargetCG>
CreateOrNull(const IntSize& aSize, gfxImageFormat aFormat);
static already_AddRefed<PrintTargetCG>
CreateOrNull(CGContextRef aContext, const IntSize& aSize);
virtual already_AddRefed<DrawTarget>
GetReferenceDrawTarget(DrawEventRecorder* aRecorder) final;
private:
PrintTargetCG(cairo_surface_t* aCairoSurface,
const IntSize& aSize);
};
} // namespace gfx
} // namespace mozilla
#endif /* MOZILLA_GFX_PRINTTARGETCG_H */

View file

@ -0,0 +1,85 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "PrintTargetPDF.h"
#include "cairo.h"
#include "cairo-pdf.h"
namespace mozilla {
namespace gfx {
static cairo_status_t
write_func(void *closure, const unsigned char *data, unsigned int length)
{
nsCOMPtr<nsIOutputStream> out = reinterpret_cast<nsIOutputStream*>(closure);
do {
uint32_t wrote = 0;
if (NS_FAILED(out->Write((const char*)data, length, &wrote))) {
break;
}
data += wrote; length -= wrote;
} while (length > 0);
NS_ASSERTION(length == 0, "not everything was written to the file");
return CAIRO_STATUS_SUCCESS;
}
PrintTargetPDF::PrintTargetPDF(cairo_surface_t* aCairoSurface,
const IntSize& aSize,
nsIOutputStream *aStream)
: PrintTarget(aCairoSurface, aSize)
, mStream(aStream)
{
}
PrintTargetPDF::~PrintTargetPDF()
{
// We get called first, then PrintTarget's dtor. That means that mStream
// is destroyed before PrintTarget's dtor calls cairo_surface_destroy. This
// can be a problem if Finish() hasn't been called on us, since
// cairo_surface_destroy will then call cairo_surface_finish and that will
// end up invoking write_func above with the by now dangling pointer mStream
// that mCairoSurface stored. To prevent that from happening we must call
// Flush here before mStream is deleted.
Finish();
}
/* static */ already_AddRefed<PrintTargetPDF>
PrintTargetPDF::CreateOrNull(nsIOutputStream *aStream,
const IntSize& aSizeInPoints)
{
cairo_surface_t* surface =
cairo_pdf_surface_create_for_stream(write_func, (void*)aStream,
aSizeInPoints.width,
aSizeInPoints.height);
if (cairo_surface_status(surface)) {
return nullptr;
}
// The new object takes ownership of our surface reference.
RefPtr<PrintTargetPDF> target = new PrintTargetPDF(surface, aSizeInPoints,
aStream);
return target.forget();
}
nsresult
PrintTargetPDF::EndPage()
{
cairo_surface_show_page(mCairoSurface);
return NS_OK;
}
void
PrintTargetPDF::Finish()
{
if (mIsFinished) {
return; // We don't want to call Close() on mStream more than once
}
PrintTarget::Finish();
mStream->Close();
}
} // namespace gfx
} // namespace mozilla

View file

@ -0,0 +1,41 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MOZILLA_GFX_PRINTTARGETPDF_H
#define MOZILLA_GFX_PRINTTARGETPDF_H
#include "nsCOMPtr.h"
#include "nsIOutputStream.h"
#include "PrintTarget.h"
namespace mozilla {
namespace gfx {
/**
* PDF printing target.
*/
class PrintTargetPDF final : public PrintTarget
{
public:
static already_AddRefed<PrintTargetPDF>
CreateOrNull(nsIOutputStream *aStream,
const IntSize& aSizeInPoints);
virtual nsresult EndPage() override;
virtual void Finish() override;
private:
PrintTargetPDF(cairo_surface_t* aCairoSurface,
const IntSize& aSize,
nsIOutputStream *aStream);
virtual ~PrintTargetPDF();
nsCOMPtr<nsIOutputStream> mStream;
};
} // namespace gfx
} // namespace mozilla
#endif /* MOZILLA_GFX_PRINTTARGETPDF_H */

View file

@ -0,0 +1,110 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "PrintTargetPS.h"
#include "cairo.h"
#include "cairo-ps.h"
namespace mozilla {
namespace gfx {
static cairo_status_t
write_func(void *closure, const unsigned char *data, unsigned int length)
{
nsCOMPtr<nsIOutputStream> out = reinterpret_cast<nsIOutputStream*>(closure);
do {
uint32_t wrote = 0;
if (NS_FAILED(out->Write((const char*)data, length, &wrote))) {
break;
}
data += wrote; length -= wrote;
} while (length > 0);
NS_ASSERTION(length == 0, "not everything was written to the file");
return CAIRO_STATUS_SUCCESS;
}
PrintTargetPS::PrintTargetPS(cairo_surface_t* aCairoSurface,
const IntSize& aSize,
nsIOutputStream *aStream,
PageOrientation aOrientation)
: PrintTarget(aCairoSurface, aSize)
, mStream(aStream)
, mOrientation(aOrientation)
{
}
PrintTargetPS::~PrintTargetPS()
{
// We get called first, then PrintTarget's dtor. That means that mStream
// is destroyed before PrintTarget's dtor calls cairo_surface_destroy. This
// can be a problem if Finish() hasn't been called on us, since
// cairo_surface_destroy will then call cairo_surface_finish and that will
// end up invoking write_func above with the by now dangling pointer mStream
// that mCairoSurface stored. To prevent that from happening we must call
// Flush here before mStream is deleted.
Finish();
}
/* static */ already_AddRefed<PrintTargetPS>
PrintTargetPS::CreateOrNull(nsIOutputStream *aStream,
IntSize aSizeInPoints,
PageOrientation aOrientation)
{
// The PS output does not specify the page size so to print landscape we need
// to rotate the drawing 90 degrees and print on portrait paper. If printing
// landscape, swap the width/height supplied to cairo to select a portrait
// print area. Our consumers are responsible for checking
// RotateForLandscape() and applying a rotation transform if true.
if (aOrientation == LANDSCAPE) {
Swap(aSizeInPoints.width, aSizeInPoints.height);
}
cairo_surface_t* surface =
cairo_ps_surface_create_for_stream(write_func, (void*)aStream,
aSizeInPoints.width,
aSizeInPoints.height);
if (cairo_surface_status(surface)) {
return nullptr;
}
cairo_ps_surface_restrict_to_level(surface, CAIRO_PS_LEVEL_2);
// The new object takes ownership of our surface reference.
RefPtr<PrintTargetPS> target = new PrintTargetPS(surface, aSizeInPoints,
aStream, aOrientation);
return target.forget();
}
nsresult
PrintTargetPS::BeginPrinting(const nsAString& aTitle,
const nsAString& aPrintToFileName)
{
if (mOrientation == PORTRAIT) {
cairo_ps_surface_dsc_comment(mCairoSurface, "%%Orientation: Portrait");
} else {
cairo_ps_surface_dsc_comment(mCairoSurface, "%%Orientation: Landscape");
}
return NS_OK;
}
nsresult
PrintTargetPS::EndPage()
{
cairo_surface_show_page(mCairoSurface);
return NS_OK;
}
void
PrintTargetPS::Finish()
{
if (mIsFinished) {
return; // We don't want to call Close() on mStream more than once
}
PrintTarget::Finish();
mStream->Close();
}
} // namespace gfx
} // namespace mozilla

View file

@ -0,0 +1,54 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MOZILLA_GFX_PRINTINGTARGETPS_H
#define MOZILLA_GFX_PRINTINGTARGETPS_H
#include "nsCOMPtr.h"
#include "nsIOutputStream.h"
#include "PrintTarget.h"
namespace mozilla {
namespace gfx {
/**
* PostScript printing target.
*/
class PrintTargetPS final : public PrintTarget {
public:
enum PageOrientation {
PORTRAIT,
LANDSCAPE
};
static already_AddRefed<PrintTargetPS>
CreateOrNull(nsIOutputStream *aStream,
IntSize aSizeInPoints,
PageOrientation aOrientation);
virtual nsresult BeginPrinting(const nsAString& aTitle,
const nsAString& aPrintToFileName) override;
virtual nsresult EndPage() override;
virtual void Finish() override;
virtual bool GetRotateForLandscape() {
return (mOrientation == LANDSCAPE);
}
private:
PrintTargetPS(cairo_surface_t* aCairoSurface,
const IntSize& aSize,
nsIOutputStream *aStream,
PageOrientation aOrientation);
virtual ~PrintTargetPS();
nsCOMPtr<nsIOutputStream> mStream;
PageOrientation mOrientation;
};
} // namespace gfx
} // namespace mozilla
#endif /* MOZILLA_GFX_PRINTINGTARGETPS_H */

View file

@ -0,0 +1,116 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "PrintTargetRecording.h"
#include "cairo.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/Logging.h"
namespace mozilla {
namespace gfx {
PrintTargetRecording::PrintTargetRecording(cairo_surface_t* aCairoSurface,
const IntSize& aSize)
: PrintTarget(aCairoSurface, aSize)
{
}
/* static */ already_AddRefed<PrintTargetRecording>
PrintTargetRecording::CreateOrNull(const IntSize& aSize)
{
if (!Factory::CheckSurfaceSize(aSize)) {
return nullptr;
}
// Perhaps surprisingly, this surface is never actually drawn to. This class
// creates a DrawTargetRecording using CreateRecordingDrawTarget, and that
// needs another DrawTarget to be passed to it. You might expect the type of
// the DrawTarget that is passed to matter because it would seem logical to
// encoded its type in the recording, and on replaying the recording a
// DrawTarget of the same type would be created. However, the passed
// DrawTarget's type doesn't seem to be encoded any more accurately than just
// "BackendType::CAIRO". Even if it were, the code that replays the
// recording is PrintTranslator::TranslateRecording which (indirectly) calls
// MakePrintTarget on the type of nsIDeviceContextSpecProxy that is created
// for the platform that we're running on, and the type of DrawTarget that
// that returns is hardcoded.
//
// The only reason that we use cairo_recording_surface_create here is:
//
// * It's pretty much the only cairo_*_surface_create methods that's both
// available on all platforms and doesn't require allocating a
// potentially large surface.
//
// * Since we need a DrawTarget to pass to CreateRecordingDrawTarget we
// might as well leverage our base class's machinery to create a
// DrawTarget (it's as good a way as any other that will work), and to do
// that we need a cairo_surface_t.
//
// So the fact that this is a "recording" PrintTarget and the function that
// we call here is cairo_recording_surface_create is simply a coincidence. We
// could use any cairo_*_surface_create method and this class would still
// work.
//
cairo_surface_t* surface =
cairo_recording_surface_create(CAIRO_CONTENT_COLOR_ALPHA, nullptr);
if (cairo_surface_status(surface)) {
return nullptr;
}
// The new object takes ownership of our surface reference.
RefPtr<PrintTargetRecording> target =
new PrintTargetRecording(surface, aSize);
return target.forget();
}
already_AddRefed<DrawTarget>
PrintTargetRecording::MakeDrawTarget(const IntSize& aSize,
DrawEventRecorder* aRecorder)
{
MOZ_ASSERT(aRecorder, "A DrawEventRecorder is required");
if (!aRecorder) {
return nullptr;
}
RefPtr<DrawTarget> dt = PrintTarget::MakeDrawTarget(aSize, nullptr);
if (dt) {
dt = CreateRecordingDrawTarget(aRecorder, dt);
if (!dt || !dt->IsValid()) {
return nullptr;
}
}
return dt.forget();
}
already_AddRefed<DrawTarget>
PrintTargetRecording::CreateRecordingDrawTarget(DrawEventRecorder* aRecorder,
DrawTarget* aDrawTarget)
{
MOZ_ASSERT(aRecorder);
MOZ_ASSERT(aDrawTarget);
RefPtr<DrawTarget> dt;
if (aRecorder) {
// It doesn't really matter what we pass as the DrawTarget here.
dt = gfx::Factory::CreateRecordingDrawTarget(aRecorder, aDrawTarget);
}
if (!dt || !dt->IsValid()) {
gfxCriticalNote
<< "Failed to create a recording DrawTarget for PrintTarget";
return nullptr;
}
return dt.forget();
}
} // namespace gfx
} // namespace mozilla

View file

@ -0,0 +1,43 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MOZILLA_GFX_PRINTTARGETRECORDING_H
#define MOZILLA_GFX_PRINTTARGETRECORDING_H
#include "PrintTarget.h"
namespace mozilla {
namespace gfx {
/**
* Recording printing target.
*
* This exists for use on e10s's content process in order to record print
* output, send it over to the parent process, and replay it on a DrawTarget
* there for printing.
*/
class PrintTargetRecording final : public PrintTarget
{
public:
static already_AddRefed<PrintTargetRecording>
CreateOrNull(const IntSize& aSize);
virtual already_AddRefed<DrawTarget>
MakeDrawTarget(const IntSize& aSize,
DrawEventRecorder* aRecorder = nullptr) override;
private:
PrintTargetRecording(cairo_surface_t* aCairoSurface,
const IntSize& aSize);
already_AddRefed<DrawTarget>
CreateRecordingDrawTarget(DrawEventRecorder* aRecorder,
DrawTarget* aDrawTarget);
};
} // namespace gfx
} // namespace mozilla
#endif /* MOZILLA_GFX_PRINTTARGETRECORDING_H */

View file

@ -0,0 +1,126 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "PrintTargetThebes.h"
#include "gfxASurface.h"
#include "gfxPlatform.h"
#include "mozilla/gfx/Logging.h"
namespace mozilla {
namespace gfx {
/* static */ already_AddRefed<PrintTargetThebes>
PrintTargetThebes::CreateOrNull(gfxASurface* aSurface)
{
MOZ_ASSERT(aSurface);
if (!aSurface || aSurface->CairoStatus()) {
return nullptr;
}
RefPtr<PrintTargetThebes> target = new PrintTargetThebes(aSurface);
return target.forget();
}
PrintTargetThebes::PrintTargetThebes(gfxASurface* aSurface)
: PrintTarget(nullptr, aSurface->GetSize())
, mGfxSurface(aSurface)
{
}
already_AddRefed<DrawTarget>
PrintTargetThebes::MakeDrawTarget(const IntSize& aSize,
DrawEventRecorder* aRecorder)
{
// This should not be called outside of BeginPage()/EndPage() calls since
// some backends can only provide a valid DrawTarget at that time.
MOZ_ASSERT(mHasActivePage, "We can't guarantee a valid DrawTarget");
RefPtr<gfx::DrawTarget> dt =
gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(mGfxSurface, aSize);
if (!dt || !dt->IsValid()) {
return nullptr;
}
if (aRecorder) {
dt = CreateRecordingDrawTarget(aRecorder, dt);
if (!dt || !dt->IsValid()) {
return nullptr;
}
}
return dt.forget();
}
already_AddRefed<DrawTarget>
PrintTargetThebes::GetReferenceDrawTarget(DrawEventRecorder* aRecorder)
{
if (!mRefDT) {
RefPtr<gfx::DrawTarget> dt =
gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(mGfxSurface, mSize);
if (!dt || !dt->IsValid()) {
return nullptr;
}
if (aRecorder) {
dt = CreateRecordingDrawTarget(aRecorder, dt);
if (!dt || !dt->IsValid()) {
return nullptr;
}
}
mRefDT = dt->CreateSimilarDrawTarget(IntSize(1,1), dt->GetFormat());
}
return do_AddRef(mRefDT);
}
nsresult
PrintTargetThebes::BeginPrinting(const nsAString& aTitle,
const nsAString& aPrintToFileName)
{
return mGfxSurface->BeginPrinting(aTitle, aPrintToFileName);
}
nsresult
PrintTargetThebes::EndPrinting()
{
return mGfxSurface->EndPrinting();
}
nsresult
PrintTargetThebes::AbortPrinting()
{
#ifdef DEBUG
mHasActivePage = false;
#endif
return mGfxSurface->AbortPrinting();
}
nsresult
PrintTargetThebes::BeginPage()
{
#ifdef DEBUG
mHasActivePage = true;
#endif
return mGfxSurface->BeginPage();
}
nsresult
PrintTargetThebes::EndPage()
{
#ifdef DEBUG
mHasActivePage = false;
#endif
return mGfxSurface->EndPage();
}
void
PrintTargetThebes::Finish()
{
return mGfxSurface->Finish();
}
} // namespace gfx
} // namespace mozilla

View file

@ -0,0 +1,57 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MOZILLA_GFX_PRINTTARGETTHEBES_H
#define MOZILLA_GFX_PRINTTARGETTHEBES_H
#include "mozilla/gfx/PrintTarget.h"
class gfxASurface;
namespace mozilla {
namespace gfx {
/**
* XXX Remove this class.
*
* This class should go away once all the logic from the gfxASurface subclasses
* has been moved to new PrintTarget subclasses and we no longer need to
* wrap a gfxASurface.
*
* When removing this class, be sure to make PrintTarget::MakeDrawTarget
* non-virtual!
*/
class PrintTargetThebes final : public PrintTarget {
public:
static already_AddRefed<PrintTargetThebes>
CreateOrNull(gfxASurface* aSurface);
virtual nsresult BeginPrinting(const nsAString& aTitle,
const nsAString& aPrintToFileName) override;
virtual nsresult EndPrinting() override;
virtual nsresult AbortPrinting() override;
virtual nsresult BeginPage() override;
virtual nsresult EndPage() override;
virtual void Finish() override;
virtual already_AddRefed<DrawTarget>
MakeDrawTarget(const IntSize& aSize,
DrawEventRecorder* aRecorder = nullptr) override;
virtual already_AddRefed<DrawTarget> GetReferenceDrawTarget(DrawEventRecorder* aRecorder) final;
private:
// Only created via CreateOrNull
explicit PrintTargetThebes(gfxASurface* aSurface);
RefPtr<gfxASurface> mGfxSurface;
};
} // namespace gfx
} // namespace mozilla
#endif /* MOZILLA_GFX_PRINTTARGETTHEBES_H */

View file

@ -0,0 +1,112 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "PrintTargetWindows.h"
#include "cairo-win32.h"
#include "mozilla/gfx/HelpersCairo.h"
#include "nsCoord.h"
#include "nsString.h"
namespace mozilla {
namespace gfx {
PrintTargetWindows::PrintTargetWindows(cairo_surface_t* aCairoSurface,
const IntSize& aSize,
HDC aDC)
: PrintTarget(aCairoSurface, aSize)
, mDC(aDC)
{
// TODO: At least add basic memory reporting.
// 4 * mSize.width * mSize.height + sizeof(PrintTargetWindows) ?
}
/* static */ already_AddRefed<PrintTargetWindows>
PrintTargetWindows::CreateOrNull(HDC aDC)
{
// Figure out the cairo surface size - Windows we need to use the printable
// area of the page. Note: we only scale the printing using the LOGPIXELSY,
// so we use that when calculating the surface width as well as the height.
int32_t heightDPI = ::GetDeviceCaps(aDC, LOGPIXELSY);
float width =
(::GetDeviceCaps(aDC, HORZRES) * POINTS_PER_INCH_FLOAT) / heightDPI;
float height =
(::GetDeviceCaps(aDC, VERTRES) * POINTS_PER_INCH_FLOAT) / heightDPI;
IntSize size = IntSize::Truncate(width, height);
if (!Factory::CheckSurfaceSize(size)) {
return nullptr;
}
cairo_surface_t* surface = cairo_win32_printing_surface_create(aDC);
if (cairo_surface_status(surface)) {
return nullptr;
}
// The new object takes ownership of our surface reference.
RefPtr<PrintTargetWindows> target =
new PrintTargetWindows(surface, size, aDC);
return target.forget();
}
nsresult
PrintTargetWindows::BeginPrinting(const nsAString& aTitle,
const nsAString& aPrintToFileName)
{
const uint32_t DOC_TITLE_LENGTH = MAX_PATH - 1;
DOCINFOW docinfo;
nsString titleStr(aTitle);
if (titleStr.Length() > DOC_TITLE_LENGTH) {
titleStr.SetLength(DOC_TITLE_LENGTH - 3);
titleStr.AppendLiteral("...");
}
nsString docName(aPrintToFileName);
docinfo.cbSize = sizeof(docinfo);
docinfo.lpszDocName = titleStr.Length() > 0 ? titleStr.get() : L"Mozilla Document";
docinfo.lpszOutput = docName.Length() > 0 ? docName.get() : nullptr;
docinfo.lpszDatatype = nullptr;
docinfo.fwType = 0;
::StartDocW(mDC, &docinfo);
return NS_OK;
}
nsresult
PrintTargetWindows::EndPrinting()
{
int result = ::EndDoc(mDC);
return (result <= 0) ? NS_ERROR_FAILURE : NS_OK;
}
nsresult
PrintTargetWindows::AbortPrinting()
{
int result = ::AbortDoc(mDC);
return (result <= 0) ? NS_ERROR_FAILURE : NS_OK;
}
nsresult
PrintTargetWindows::BeginPage()
{
int result = ::StartPage(mDC);
return (result <= 0) ? NS_ERROR_FAILURE : NS_OK;
}
nsresult
PrintTargetWindows::EndPage()
{
cairo_surface_show_page(mCairoSurface);
int result = ::EndPage(mDC);
return (result <= 0) ? NS_ERROR_FAILURE : NS_OK;
}
} // namespace gfx
} // namespace mozilla

View file

@ -0,0 +1,43 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MOZILLA_GFX_PRINTTARGETWINDOWS_H
#define MOZILLA_GFX_PRINTTARGETWINDOWS_H
#include "PrintTarget.h"
/* include windows.h for the HDC definitions that we need. */
#include <windows.h>
namespace mozilla {
namespace gfx {
/**
* Windows printing target.
*/
class PrintTargetWindows final : public PrintTarget
{
public:
static already_AddRefed<PrintTargetWindows>
CreateOrNull(HDC aDC);
virtual nsresult BeginPrinting(const nsAString& aTitle,
const nsAString& aPrintToFileName) override;
virtual nsresult EndPrinting() override;
virtual nsresult AbortPrinting() override;
virtual nsresult BeginPage() override;
virtual nsresult EndPage() override;
private:
PrintTargetWindows(cairo_surface_t* aCairoSurface,
const IntSize& aSize,
HDC aDC);
HDC mDC;
};
} // namespace gfx
} // namespace mozilla
#endif /* MOZILLA_GFX_PRINTTARGETWINDOWS_H */

43
gfx/thebes/RoundedRect.h Normal file
View file

@ -0,0 +1,43 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gfxRect.h"
#include "mozilla/gfx/PathHelpers.h"
namespace mozilla {
/* A rounded rectangle abstraction.
*
* This can represent a rectangle with a different pair of radii on each corner.
*
* Note: CoreGraphics and Direct2D only support rounded rectangle with the same
* radii on all corners. However, supporting CSS's border-radius requires the extra flexibility. */
struct RoundedRect {
typedef mozilla::gfx::RectCornerRadii RectCornerRadii;
RoundedRect(gfxRect &aRect, RectCornerRadii &aCorners) : rect(aRect), corners(aCorners) { }
void Deflate(gfxFloat aTopWidth, gfxFloat aBottomWidth, gfxFloat aLeftWidth, gfxFloat aRightWidth) {
// deflate the internal rect
rect.x += aLeftWidth;
rect.y += aTopWidth;
rect.width = std::max(0., rect.width - aLeftWidth - aRightWidth);
rect.height = std::max(0., rect.height - aTopWidth - aBottomWidth);
corners.radii[NS_CORNER_TOP_LEFT].width = std::max(0., corners.radii[NS_CORNER_TOP_LEFT].width - aLeftWidth);
corners.radii[NS_CORNER_TOP_LEFT].height = std::max(0., corners.radii[NS_CORNER_TOP_LEFT].height - aTopWidth);
corners.radii[NS_CORNER_TOP_RIGHT].width = std::max(0., corners.radii[NS_CORNER_TOP_RIGHT].width - aRightWidth);
corners.radii[NS_CORNER_TOP_RIGHT].height = std::max(0., corners.radii[NS_CORNER_TOP_RIGHT].height - aTopWidth);
corners.radii[NS_CORNER_BOTTOM_LEFT].width = std::max(0., corners.radii[NS_CORNER_BOTTOM_LEFT].width - aLeftWidth);
corners.radii[NS_CORNER_BOTTOM_LEFT].height = std::max(0., corners.radii[NS_CORNER_BOTTOM_LEFT].height - aBottomWidth);
corners.radii[NS_CORNER_BOTTOM_RIGHT].width = std::max(0., corners.radii[NS_CORNER_BOTTOM_RIGHT].width - aRightWidth);
corners.radii[NS_CORNER_BOTTOM_RIGHT].height = std::max(0., corners.radii[NS_CORNER_BOTTOM_RIGHT].height - aBottomWidth);
}
gfxRect rect;
RectCornerRadii corners;
};
} // namespace mozilla

View file

@ -0,0 +1,150 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include "SoftwareVsyncSource.h"
#include "base/task.h"
#include "gfxPlatform.h"
#include "nsThreadUtils.h"
using namespace mozilla;
SoftwareVsyncSource::SoftwareVsyncSource()
{
MOZ_ASSERT(NS_IsMainThread());
mGlobalDisplay = new SoftwareDisplay();
}
SoftwareVsyncSource::~SoftwareVsyncSource()
{
MOZ_ASSERT(NS_IsMainThread());
mGlobalDisplay = nullptr;
}
SoftwareDisplay::SoftwareDisplay()
: mVsyncEnabled(false)
{
// Mimic 60 fps
MOZ_ASSERT(NS_IsMainThread());
const double rate = 1000.0 / (double) gfxPlatform::GetSoftwareVsyncRate();
mVsyncRate = mozilla::TimeDuration::FromMilliseconds(rate);
mVsyncThread = new base::Thread("SoftwareVsyncThread");
MOZ_RELEASE_ASSERT(mVsyncThread->Start(), "GFX: Could not start software vsync thread");
}
SoftwareDisplay::~SoftwareDisplay() {}
void
SoftwareDisplay::EnableVsync()
{
MOZ_ASSERT(mVsyncThread->IsRunning());
if (NS_IsMainThread()) {
if (mVsyncEnabled) {
return;
}
mVsyncEnabled = true;
mVsyncThread->message_loop()->PostTask(
NewRunnableMethod(this, &SoftwareDisplay::EnableVsync));
return;
}
MOZ_ASSERT(IsInSoftwareVsyncThread());
NotifyVsync(mozilla::TimeStamp::Now());
}
void
SoftwareDisplay::DisableVsync()
{
MOZ_ASSERT(mVsyncThread->IsRunning());
if (NS_IsMainThread()) {
if (!mVsyncEnabled) {
return;
}
mVsyncEnabled = false;
mVsyncThread->message_loop()->PostTask(
NewRunnableMethod(this, &SoftwareDisplay::DisableVsync));
return;
}
MOZ_ASSERT(IsInSoftwareVsyncThread());
if (mCurrentVsyncTask) {
mCurrentVsyncTask->Cancel();
mCurrentVsyncTask = nullptr;
}
}
bool
SoftwareDisplay::IsVsyncEnabled()
{
MOZ_ASSERT(NS_IsMainThread());
return mVsyncEnabled;
}
bool
SoftwareDisplay::IsInSoftwareVsyncThread()
{
return mVsyncThread->thread_id() == PlatformThread::CurrentId();
}
void
SoftwareDisplay::NotifyVsync(mozilla::TimeStamp aVsyncTimestamp)
{
MOZ_ASSERT(IsInSoftwareVsyncThread());
mozilla::TimeStamp displayVsyncTime = aVsyncTimestamp;
mozilla::TimeStamp now = mozilla::TimeStamp::Now();
// Posted tasks can only have integer millisecond delays
// whereas TimeDurations can have floating point delays.
// Thus the vsync timestamp can be in the future, which large parts
// of the system can't handle, including animations. Force the timestamp to be now.
if (aVsyncTimestamp > now) {
displayVsyncTime = now;
}
Display::NotifyVsync(displayVsyncTime);
// Prevent skew by still scheduling based on the original
// vsync timestamp
ScheduleNextVsync(aVsyncTimestamp);
}
mozilla::TimeDuration
SoftwareDisplay::GetVsyncRate()
{
return mVsyncRate;
}
void
SoftwareDisplay::ScheduleNextVsync(mozilla::TimeStamp aVsyncTimestamp)
{
MOZ_ASSERT(IsInSoftwareVsyncThread());
mozilla::TimeStamp nextVsync = aVsyncTimestamp + mVsyncRate;
mozilla::TimeDuration delay = nextVsync - mozilla::TimeStamp::Now();
if (delay.ToMilliseconds() < 0) {
delay = mozilla::TimeDuration::FromMilliseconds(0);
nextVsync = mozilla::TimeStamp::Now();
}
mCurrentVsyncTask =
NewCancelableRunnableMethod<mozilla::TimeStamp>(this,
&SoftwareDisplay::NotifyVsync,
nextVsync);
RefPtr<Runnable> addrefedTask = mCurrentVsyncTask;
mVsyncThread->message_loop()->PostDelayedTask(
addrefedTask.forget(),
delay.ToMilliseconds());
}
void
SoftwareDisplay::Shutdown()
{
MOZ_ASSERT(NS_IsMainThread());
DisableVsync();
mVsyncThread->Stop();
delete mVsyncThread;
}

View file

@ -0,0 +1,62 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#ifndef GFX_SOFTWARE_VSYNC_SOURCE_H
#define GFX_SOFTWARE_VSYNC_SOURCE_H
#include "mozilla/Monitor.h"
#include "mozilla/RefPtr.h"
#include "mozilla/TimeStamp.h"
#include "base/thread.h"
#include "nsISupportsImpl.h"
#include "VsyncSource.h"
class SoftwareDisplay final : public mozilla::gfx::VsyncSource::Display
{
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SoftwareDisplay)
public:
SoftwareDisplay();
virtual void EnableVsync() override;
virtual void DisableVsync() override;
virtual bool IsVsyncEnabled() override;
bool IsInSoftwareVsyncThread();
virtual void NotifyVsync(mozilla::TimeStamp aVsyncTimestamp) override;
virtual mozilla::TimeDuration GetVsyncRate() override;
void ScheduleNextVsync(mozilla::TimeStamp aVsyncTimestamp);
void Shutdown() override;
protected:
~SoftwareDisplay();
private:
mozilla::TimeDuration mVsyncRate;
// Use a chromium thread because nsITimers* fire on the main thread
base::Thread* mVsyncThread;
RefPtr<mozilla::CancelableRunnable> mCurrentVsyncTask; // only access on vsync thread
bool mVsyncEnabled; // Only access on main thread
}; // SoftwareDisplay
// Fallback option to use a software timer to mimic vsync. Useful for gtests
// To mimic a hardware vsync thread, we create a dedicated software timer
// vsync thread.
class SoftwareVsyncSource : public mozilla::gfx::VsyncSource
{
public:
SoftwareVsyncSource();
~SoftwareVsyncSource();
virtual Display& GetGlobalDisplay() override
{
MOZ_ASSERT(mGlobalDisplay != nullptr);
return *mGlobalDisplay;
}
private:
RefPtr<SoftwareDisplay> mGlobalDisplay;
};
#endif /* GFX_SOFTWARE_VSYNC_SOURCE_H */

153
gfx/thebes/VsyncSource.cpp Normal file
View file

@ -0,0 +1,153 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "VsyncSource.h"
#include "nsThreadUtils.h"
#include "nsXULAppAPI.h"
#include "mozilla/VsyncDispatcher.h"
#include "MainThreadUtils.h"
namespace mozilla {
namespace gfx {
void
VsyncSource::AddCompositorVsyncDispatcher(CompositorVsyncDispatcher* aCompositorVsyncDispatcher)
{
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
// Just use the global display until we have enough information to get the
// corresponding display for compositor.
GetGlobalDisplay().AddCompositorVsyncDispatcher(aCompositorVsyncDispatcher);
}
void
VsyncSource::RemoveCompositorVsyncDispatcher(CompositorVsyncDispatcher* aCompositorVsyncDispatcher)
{
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
// See also AddCompositorVsyncDispatcher().
GetGlobalDisplay().RemoveCompositorVsyncDispatcher(aCompositorVsyncDispatcher);
}
RefPtr<RefreshTimerVsyncDispatcher>
VsyncSource::GetRefreshTimerVsyncDispatcher()
{
MOZ_ASSERT(XRE_IsParentProcess());
// See also AddCompositorVsyncDispatcher().
return GetGlobalDisplay().GetRefreshTimerVsyncDispatcher();
}
VsyncSource::Display::Display()
: mDispatcherLock("display dispatcher lock")
, mRefreshTimerNeedsVsync(false)
{
MOZ_ASSERT(NS_IsMainThread());
mRefreshTimerVsyncDispatcher = new RefreshTimerVsyncDispatcher();
}
VsyncSource::Display::~Display()
{
MOZ_ASSERT(NS_IsMainThread());
MutexAutoLock lock(mDispatcherLock);
mRefreshTimerVsyncDispatcher = nullptr;
mCompositorVsyncDispatchers.Clear();
}
void
VsyncSource::Display::NotifyVsync(TimeStamp aVsyncTimestamp)
{
// Called on the vsync thread
MutexAutoLock lock(mDispatcherLock);
for (size_t i = 0; i < mCompositorVsyncDispatchers.Length(); i++) {
mCompositorVsyncDispatchers[i]->NotifyVsync(aVsyncTimestamp);
}
mRefreshTimerVsyncDispatcher->NotifyVsync(aVsyncTimestamp);
}
TimeDuration
VsyncSource::Display::GetVsyncRate()
{
// If hardware queries fail / are unsupported, we have to just guess.
return TimeDuration::FromMilliseconds(1000.0 / 60.0);
}
void
VsyncSource::Display::AddCompositorVsyncDispatcher(CompositorVsyncDispatcher* aCompositorVsyncDispatcher)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aCompositorVsyncDispatcher);
{ // scope lock
MutexAutoLock lock(mDispatcherLock);
if (!mCompositorVsyncDispatchers.Contains(aCompositorVsyncDispatcher)) {
mCompositorVsyncDispatchers.AppendElement(aCompositorVsyncDispatcher);
}
}
UpdateVsyncStatus();
}
void
VsyncSource::Display::RemoveCompositorVsyncDispatcher(CompositorVsyncDispatcher* aCompositorVsyncDispatcher)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aCompositorVsyncDispatcher);
{ // Scope lock
MutexAutoLock lock(mDispatcherLock);
if (mCompositorVsyncDispatchers.Contains(aCompositorVsyncDispatcher)) {
mCompositorVsyncDispatchers.RemoveElement(aCompositorVsyncDispatcher);
}
}
UpdateVsyncStatus();
}
void
VsyncSource::Display::NotifyRefreshTimerVsyncStatus(bool aEnable)
{
MOZ_ASSERT(NS_IsMainThread());
mRefreshTimerNeedsVsync = aEnable;
UpdateVsyncStatus();
}
void
VsyncSource::Display::UpdateVsyncStatus()
{
MOZ_ASSERT(NS_IsMainThread());
// WARNING: This function SHOULD NOT BE CALLED WHILE HOLDING LOCKS
// NotifyVsync grabs a lock to dispatch vsync events
// When disabling vsync, we wait for the underlying thread to stop on some platforms
// We can deadlock if we wait for the underlying vsync thread to stop
// while the vsync thread is in NotifyVsync.
bool enableVsync = false;
{ // scope lock
MutexAutoLock lock(mDispatcherLock);
enableVsync = !mCompositorVsyncDispatchers.IsEmpty() || mRefreshTimerNeedsVsync;
}
if (enableVsync) {
EnableVsync();
} else {
DisableVsync();
}
if (IsVsyncEnabled() != enableVsync) {
NS_WARNING("Vsync status did not change.");
}
}
RefPtr<RefreshTimerVsyncDispatcher>
VsyncSource::Display::GetRefreshTimerVsyncDispatcher()
{
return mRefreshTimerVsyncDispatcher;
}
void
VsyncSource::Shutdown()
{
GetGlobalDisplay().Shutdown();
}
} //namespace gfx
} //namespace mozilla

84
gfx/thebes/VsyncSource.h Normal file
View file

@ -0,0 +1,84 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_VSYNCSOURCE_H
#define GFX_VSYNCSOURCE_H
#include "nsTArray.h"
#include "mozilla/RefPtr.h"
#include "mozilla/Mutex.h"
#include "mozilla/TimeStamp.h"
#include "nsISupportsImpl.h"
namespace mozilla {
class RefreshTimerVsyncDispatcher;
class CompositorVsyncDispatcher;
namespace gfx {
// Controls how and when to enable/disable vsync. Lives as long as the
// gfxPlatform does on the parent process
class VsyncSource
{
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VsyncSource)
typedef mozilla::RefreshTimerVsyncDispatcher RefreshTimerVsyncDispatcher;
typedef mozilla::CompositorVsyncDispatcher CompositorVsyncDispatcher;
public:
// Controls vsync unique to each display and unique on each platform
class Display {
public:
Display();
virtual ~Display();
// Notified when this display's vsync occurs, on the vsync thread
// The aVsyncTimestamp should normalize to the Vsync time that just occured
// However, different platforms give different vsync notification times.
// b2g - The vsync timestamp of the previous frame that was just displayed
// OSX - The vsync timestamp of the upcoming frame, in the future
// Windows: It's messy, see gfxWindowsPlatform.
// Android: TODO
// All platforms should normalize to the vsync that just occured.
// Large parts of Gecko assume TimeStamps should not be in the future such as animations
virtual void NotifyVsync(TimeStamp aVsyncTimestamp);
RefPtr<RefreshTimerVsyncDispatcher> GetRefreshTimerVsyncDispatcher();
void AddCompositorVsyncDispatcher(CompositorVsyncDispatcher* aCompositorVsyncDispatcher);
void RemoveCompositorVsyncDispatcher(CompositorVsyncDispatcher* aCompositorVsyncDispatcher);
void NotifyRefreshTimerVsyncStatus(bool aEnable);
virtual TimeDuration GetVsyncRate();
// These should all only be called on the main thread
virtual void EnableVsync() = 0;
virtual void DisableVsync() = 0;
virtual bool IsVsyncEnabled() = 0;
virtual void Shutdown() = 0;
private:
void UpdateVsyncStatus();
Mutex mDispatcherLock;
bool mRefreshTimerNeedsVsync;
nsTArray<RefPtr<CompositorVsyncDispatcher>> mCompositorVsyncDispatchers;
RefPtr<RefreshTimerVsyncDispatcher> mRefreshTimerVsyncDispatcher;
};
void AddCompositorVsyncDispatcher(CompositorVsyncDispatcher* aCompositorVsyncDispatcher);
void RemoveCompositorVsyncDispatcher(CompositorVsyncDispatcher* aCompositorVsyncDispatcher);
RefPtr<RefreshTimerVsyncDispatcher> GetRefreshTimerVsyncDispatcher();
virtual Display& GetGlobalDisplay() = 0; // Works across all displays
void Shutdown();
protected:
virtual ~VsyncSource() {}
};
} // namespace gfx
} // namespace mozilla
#endif /* GFX_VSYNCSOURCE_H */

View file

@ -0,0 +1,119 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef CAIROXLIBUTILS_H_
#define CAIROXLIBUTILS_H_
#include "cairo.h"
#include <X11/Xlib.h>
CAIRO_BEGIN_DECLS
/**
* This callback encapsulates Xlib-based rendering. We assume that the
* execution of the callback is equivalent to compositing some RGBA image of
* size (bounds_width, bounds_height) onto the drawable at offset (offset_x,
* offset_y), clipped to the union of the clip_rects if num_rects is greater
* than zero. This includes the assumption that the same RGBA image
* is composited if you call the callback multiple times with the same closure,
* display and visual during a single cairo_draw_with_xlib call.
*
* @return True on success, False on non-recoverable error
*/
typedef cairo_bool_t (* cairo_xlib_drawing_callback)
(void *closure,
Screen *screen,
Drawable drawable,
Visual *visual,
short offset_x, short offset_y,
XRectangle* clip_rects, unsigned int num_rects);
/**
* This structure captures the result of the native drawing, in case the
* caller may wish to reapply the drawing efficiently later.
*/
typedef struct {
cairo_surface_t *surface;
cairo_bool_t uniform_alpha;
cairo_bool_t uniform_color;
double alpha; /* valid only if uniform_alpha is TRUE */
double r, g, b; /* valid only if uniform_color is TRUE */
} cairo_xlib_drawing_result_t;
/**
* This type specifies whether the native drawing callback draws all pixels
* in its bounds opaquely, independent of the contents of the target drawable,
* or whether it leaves pixels transparent/translucent or depends on the
* existing contents of the target drawable in some way.
*/
typedef enum _cairo_xlib_drawing_opacity {
CAIRO_XLIB_DRAWING_OPAQUE,
CAIRO_XLIB_DRAWING_TRANSPARENT
} cairo_xlib_drawing_opacity_t;
/**
* This type encodes the capabilities of the native drawing callback.
*
* If CAIRO_XLIB_DRAWING_SUPPORTS_OFFSET is set, 'offset_x' and 'offset_y'
* can be nonzero in the call to the callback; otherwise they will be zero.
*
* If CAIRO_XLIB_DRAWING_SUPPORTS_CLIP_RECT is set, then 'num_rects' can be
* zero or one in the call to the callback. If
* CAIRO_XLIB_DRAWING_SUPPORTS_CLIP_LIST is set, then 'num_rects' can be
* anything in the call to the callback. Otherwise 'num_rects' will be zero.
* Do not set both of these values.
*
* If CAIRO_XLIB_DRAWING_SUPPORTS_ALTERNATE_SCREEN is set, then 'screen' can
* be any screen on any display, otherwise it will be the default screen of
* the display passed into cairo_draw_with_xlib.
*
* If CAIRO_XLIB_DRAWING_SUPPORTS_NONDEFAULT_VISUAL is set, then 'visual' can be
* any visual, otherwise it will be equal to
* DefaultVisualOfScreen (screen).
*/
typedef enum {
CAIRO_XLIB_DRAWING_SUPPORTS_OFFSET = 0x01,
CAIRO_XLIB_DRAWING_SUPPORTS_CLIP_RECT = 0x02,
CAIRO_XLIB_DRAWING_SUPPORTS_CLIP_LIST = 0x04,
CAIRO_XLIB_DRAWING_SUPPORTS_ALTERNATE_SCREEN = 0x08,
CAIRO_XLIB_DRAWING_SUPPORTS_NONDEFAULT_VISUAL = 0x10
} cairo_xlib_drawing_support_t;
/**
* Draw Xlib output into any cairo context. All cairo transforms and effects
* are honored, including the current operator. This is equivalent to a
* cairo_set_source_surface and then cairo_paint.
* @param cr the context to draw into
* @param callback the code to perform Xlib rendering
* @param closure associated data
* @param dpy an X display to use in case the cairo context has no associated X display
* @param width the width of the putative image drawn by the callback
* @param height the height of the putative image drawn by the callback
* @param is_opaque set to CAIRO_XLIB_DRAWING_IS_OPAQUE to indicate
* that all alpha values of the putative image will be 1.0; the pixels drawn into
* the Drawable must not depend on the prior contents of the Drawable
* in any way
* @param capabilities the capabilities of the callback as described above.
* @param result if non-NULL, we *may* fill in the struct with information about
* the rendered image. 'surface' may be filled in with a surface representing
* the image, similar to the target of 'cr'. If 'uniform_alpha' is True then
* every pixel of the image has the same alpha value 'alpha'. If
* 'uniform_color' is True then every pixel of the image has the same RGB
* color (r, g, b). If the image has uniform color and alpha (or alpha is zero,
* in which case the color is always uniform) then we won't bother returning
* a surface for it.
*/
void cairo_draw_with_xlib (cairo_t *cr,
cairo_xlib_drawing_callback callback,
void *closure,
Display *dpy,
unsigned int width, unsigned int height,
cairo_xlib_drawing_opacity_t is_opaque,
cairo_xlib_drawing_support_t capabilities,
cairo_xlib_drawing_result_t *result);
CAIRO_END_DECLS
#endif /*CAIROXLIBUTILS_H_*/

View file

@ -0,0 +1,168 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* This file is based on a header file that was briefly seen in the
* Windows 8 RC SDK. The work for this file itself was based on the one in ProcessHacker at
* http://processhacker.svn.sourceforge.net/viewvc/processhacker/2.x/trunk/plugins/ExtendedTools/d3dkmt.h?revision=4758&view=markup
* For more details see Mozilla Bug 689870.
* [Bug 917496 indicates that some of these structs may not match reality, and
* therefore should not be trusted. See the reference to bug 917496 in
* gfxWindowsPlatform.cpp.]
*/
typedef struct _D3DKMTQS_COUNTER
{
ULONG Count;
ULONGLONG Bytes;
} D3DKMTQS_COUNTER;
typedef struct _D3DKMTQS_ADAPTER_INFO
{
ULONG NbSegments;
ULONG Filler[4];
ULONGLONG Filler2[2]; // Assumed sizeof(LONGLONG) = sizeof(ULONGLONG)
struct {
ULONG Filler[14];
} Filler_RDMAB;
struct {
ULONG Filler[9];
} Filler_R;
struct {
ULONG Filler[4];
D3DKMTQS_COUNTER Filler2;
} Filler_P;
struct {
D3DKMTQS_COUNTER Filler[16];
ULONG Filler2[2];
} Filler_PF;
struct {
ULONGLONG Filler[8];
} Filler_PT;
struct {
ULONG Filler[2];
} Filler_SR;
struct {
ULONG Filler[7];
} Filler_L;
struct {
D3DKMTQS_COUNTER Filler[7];
} Filler_A;
struct {
D3DKMTQS_COUNTER Filler[4];
} Filler_T;
ULONG64 Reserved[8];
} D3DKMTQS_ADAPTER_INFO;
typedef struct _D3DKMTQS_SEGMENT_INFO_WIN7
{
ULONG Filler[3];
struct {
ULONGLONG Filler;
ULONG Filler2[2];
} Filler_M;
ULONG Aperture;
ULONGLONG Filler3[5];
ULONG64 Filler4[8];
} D3DKMTQS_SEGMENT_INFO_WIN7;
typedef struct _D3DKMTQS_SEGMENT_INFO_WIN8
{
ULONGLONG Filler[3];
struct {
ULONGLONG Filler;
ULONG Filler2[2];
} Filler_M;
ULONG Aperture;
ULONGLONG Filler3[5];
ULONG64 Filler4[8];
} D3DKMTQS_SEGMENT_INFO_WIN8;
typedef struct _D3DKMTQS_SYSTEM_MEMORY
{
ULONGLONG BytesAllocated;
ULONG Filler[2];
ULONGLONG Filler2[7];
} D3DKMTQS_SYSTEM_MEMORY;
typedef struct _D3DKMTQS_PROCESS_INFO
{
ULONG Filler[2];
struct {
ULONGLONG BytesAllocated;
ULONG Filler[2];
ULONGLONG Filler2[7];
} SystemMemory;
ULONG64 Reserved[8];
} D3DKMTQS_PROCESS_INFO;
typedef struct _D3DKMTQS_PROCESS_SEGMENT_INFO
{
union {
struct {
ULONGLONG BytesCommitted;
} Win8;
struct {
ULONG BytesCommitted;
ULONG UnknownRandomness;
} Win7;
};
ULONGLONG Filler[2];
ULONG Filler2;
struct {
ULONG Filler;
D3DKMTQS_COUNTER Filler2[6];
ULONGLONG Filler3;
} Filler3;
struct {
ULONGLONG Filler;
} Filler4;
ULONG64 Reserved[8];
} D3DKMTQS_PROCESS_SEGMENT_INFO;
typedef enum _D3DKMTQS_TYPE
{
D3DKMTQS_ADAPTER = 0,
D3DKMTQS_PROCESS = 1,
D3DKMTQS_SEGMENT = 3,
D3DKMTQS_PROCESS_SEGMENT = 4,
} D3DKMTQS_TYPE;
typedef union _D3DKMTQS_RESULT
{
D3DKMTQS_ADAPTER_INFO AdapterInfo;
D3DKMTQS_SEGMENT_INFO_WIN7 SegmentInfoWin7;
D3DKMTQS_SEGMENT_INFO_WIN8 SegmentInfoWin8;
D3DKMTQS_PROCESS_INFO ProcessInfo;
D3DKMTQS_PROCESS_SEGMENT_INFO ProcessSegmentInfo;
} D3DKMTQS_RESULT;
typedef struct _D3DKMTQS_QUERY_SEGMENT
{
ULONG SegmentId;
} D3DKMTQS_QUERY_SEGMENT;
typedef struct _D3DKMTQS
{
D3DKMTQS_TYPE Type;
LUID AdapterLuid;
HANDLE hProcess;
D3DKMTQS_RESULT QueryResult;
union
{
D3DKMTQS_QUERY_SEGMENT QuerySegment;
D3DKMTQS_QUERY_SEGMENT QueryProcessSegment;
};
} D3DKMTQS;
extern "C" {
typedef __checkReturn NTSTATUS (APIENTRY *PFND3DKMTQS)(const D3DKMTQS *);
}

View file

@ -0,0 +1,86 @@
#!/usr/bin/env perl
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# This tool is used to prepare a list of valid language subtags from the
# IANA registry (http://www.iana.org/assignments/language-subtag-registry).
# Run as
#
# perl genLanguageTagList.pl language-subtag-registry > gfxLanguageTagList.cpp
#
# where language-subtag-registry is a copy of the IANA registry file.
use strict;
my $timestamp = gmtime();
print <<__END;
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* Derived from the IANA language subtag registry by genLanguageTagList.pl.
*
* Created on $timestamp.
*
* * * * * This file contains MACHINE-GENERATED DATA, do not edit! * * * * *
*/
__END
my $isLanguage = 0;
while (<>) {
# strip leading/trailing whitespace, if any
chomp;
s/^\s+//;
s/\s+$//;
# assume File-Date precedes the actual list;
# record the date, and begin assignment to an array of valid tags
if (m/^File-Date:\s*(.+)$/) {
print "// Based on IANA registry dated $1\n\n";
print "static const uint32_t sLanguageTagList[] = {";
next;
}
if (m/^%%/) {
$isLanguage = 0;
next;
}
# we only care about records of type 'language'
if (m/^Type:\s*(.+)$/) {
$isLanguage = ($1 eq 'language');
next;
}
# append the tag to our string, with ";" as a delimiter
if ($isLanguage && m/^Subtag:\s*([a-z]{2,3})\s*$/) {
my $tagstr = $1;
print "\n TRUETYPE_TAG(",
join(",", map { $_ eq " " ? " 0 " : "'" . $_ . "'" } split(//, substr($tagstr . " ", 0, 4))),
"), // ", $tagstr;
next;
}
if ($isLanguage && m/^Description:\s*(.+)$/) {
print " = $1";
$isLanguage = 0; # only print first Description field
next;
}
}
# at end of file, terminate our assignment to the array of tags
print <<__END;
0x0 // end of language code list
};
/*
* * * * * This file contains MACHINE-GENERATED DATA, do not edit! * * * * *
*/
__END

22
gfx/thebes/genTables.py Normal file
View file

@ -0,0 +1,22 @@
#!/usr/bin/python
from __future__ import print_function
import sys
def table_generator(f):
return ",\n".join([", ".join(["0x%2.2x" % h for h in [f(i) for i in range(r,r+16)]]) for r in range(0, 65536, 16)])
def generate(output):
output.write("const uint8_t gfxUtils::sPremultiplyTable[256*256] = {\n");
output.write(table_generator(lambda i: ((i / 256) * (i % 256) + 254) / 255) + "\n")
output.write("};\n");
output.write("const uint8_t gfxUtils::sUnpremultiplyTable[256*256] = {\n");
output.write(table_generator(lambda i: (i % 256) * 255 / ((i / 256) if (i / 256) > 0 else 255) % 256) + "\n")
output.write("};\n");
if __name__ == '__main__':
if len(sys.argv) != 2:
print("Usage: genTables.py <header.h>", file=sys.stderr)
sys.exit(1)
with open(sys.argv[1], 'w') as f:
generate(f)

77
gfx/thebes/gencjkcisvs.py Normal file
View file

@ -0,0 +1,77 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
import os.path
import re
import sys
f = open(sys.argv[1] if len(sys.argv) > 1 else 'StandardizedVariants.txt')
line = f.readline()
m = re.compile('^# (StandardizedVariants(-\d+(\.\d+)*)?\.txt)').search(line)
fileversion = m.group(1)
vsdict = {}
r = re.compile('^([0-9A-F]{4,6}) (FE0[0-9A-F]); CJK COMPATIBILITY IDEOGRAPH-([0-9A-F]{4,6});')
while True:
line = f.readline()
if not line:
break
if not 'CJK COMPATIBILITY IDEOGRAPH-' in line:
continue
m = r.search(line)
unified = int(m.group(1), 16)
vs = int(m.group(2), 16)
compat = int(m.group(3), 16)
if not vs in vsdict:
vsdict[vs] = {}
vsdict[vs][unified] = compat
f.close
offsets = []
length = 10 + 11 * len(vsdict)
for (k, mappings) in sorted(vsdict.items()):
offsets.append(length)
length += 4 + 5 * len(mappings)
f = open(sys.argv[2] if len(sys.argv) > 2 else 'CJKCompatSVS.cpp', 'wb')
f.write("""// Generated by %s. Do not edit.
#include <stdint.h>
#define U16(v) (((v) >> 8) & 0xFF), ((v) & 0xFF)
#define U24(v) (((v) >> 16) & 0xFF), (((v) >> 8) & 0xFF), ((v) & 0xFF)
#define U32(v) (((v) >> 24) & 0xFF), (((v) >> 16) & 0xFF), (((v) >> 8) & 0xFF), ((v) & 0xFF)
#define GLYPH(v) U16(v >= 0x2F800 ? (v) - (0x2F800 - 0xFB00) : (v))
// Fallback mappings for CJK Compatibility Ideographs Standardized Variants
// taken from %s.
// Using OpenType format 14 cmap subtable structure to reuse the lookup code
// for fonts. The glyphID field is used to store the corresponding codepoints
// CJK Compatibility Ideographs. To fit codepoints into the 16-bit glyphID
// field, CJK Compatibility Ideographs Supplement (U+2F800..U+2FA1F) will be
// mapped to 0xFB00..0xFD1F.
extern const uint8_t sCJKCompatSVSTable[] = {
""" % (os.path.basename(sys.argv[0]), fileversion))
f.write(' U16(14), // format\n')
f.write(' U32(%d), // length\n' % length)
f.write(' U32(%d), // numVarSelectorRecords\n' % len(vsdict))
for i, k in enumerate(sorted(vsdict.keys())):
f.write(' U24(0x%04X), U32(0), U32(%d), // varSelectorRecord[%d]\n' % (k, offsets[i], i))
for (k, mappings) in sorted(vsdict.items()):
f.write(' // 0x%04X\n' % k)
f.write(' U32(%d), // numUVSMappings\n' % len(mappings))
for (unified, compat) in sorted(mappings.items()):
f.write(' U24(0x%04X), GLYPH(0x%04X),\n' % (unified, compat))
f.write("""};
#undef U16
#undef U24
#undef U32
#undef GLYPH
static_assert(sizeof sCJKCompatSVSTable == %d, "Table generator has a bug.");
""" % length)

131
gfx/thebes/gfx2DGlue.h Normal file
View file

@ -0,0 +1,131 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_2D_GLUE_H
#define GFX_2D_GLUE_H
#include "gfxPlatform.h"
#include "gfxRect.h"
#include "gfxMatrix.h"
#include "gfxContext.h"
#include "mozilla/gfx/Matrix.h"
#include "mozilla/gfx/Rect.h"
#include "mozilla/gfx/2D.h"
namespace mozilla {
namespace gfx {
inline Rect ToRect(const gfxRect &aRect)
{
return Rect(Float(aRect.x), Float(aRect.y),
Float(aRect.width), Float(aRect.height));
}
inline RectDouble ToRectDouble(const gfxRect &aRect)
{
return RectDouble(aRect.x, aRect.y, aRect.width, aRect.height);
}
inline Matrix ToMatrix(const gfxMatrix &aMatrix)
{
return Matrix(Float(aMatrix._11), Float(aMatrix._12), Float(aMatrix._21),
Float(aMatrix._22), Float(aMatrix._31), Float(aMatrix._32));
}
inline gfxMatrix ThebesMatrix(const Matrix &aMatrix)
{
return gfxMatrix(aMatrix._11, aMatrix._12, aMatrix._21,
aMatrix._22, aMatrix._31, aMatrix._32);
}
inline Point ToPoint(const gfxPoint &aPoint)
{
return Point(Float(aPoint.x), Float(aPoint.y));
}
inline Size ToSize(const gfxSize &aSize)
{
return Size(Float(aSize.width), Float(aSize.height));
}
inline gfxPoint ThebesPoint(const Point &aPoint)
{
return gfxPoint(aPoint.x, aPoint.y);
}
inline gfxSize ThebesSize(const Size &aSize)
{
return gfxSize(aSize.width, aSize.height);
}
inline gfxRect ThebesRect(const Rect &aRect)
{
return gfxRect(aRect.x, aRect.y, aRect.width, aRect.height);
}
inline gfxRect ThebesRect(const IntRect &aRect)
{
return gfxRect(aRect.x, aRect.y, aRect.width, aRect.height);
}
inline gfxRect ThebesRect(const RectDouble &aRect)
{
return gfxRect(aRect.x, aRect.y, aRect.width, aRect.height);
}
inline gfxImageFormat SurfaceFormatToImageFormat(SurfaceFormat aFormat)
{
switch (aFormat) {
case SurfaceFormat::B8G8R8A8:
return SurfaceFormat::A8R8G8B8_UINT32;
case SurfaceFormat::B8G8R8X8:
return SurfaceFormat::X8R8G8B8_UINT32;
case SurfaceFormat::R5G6B5_UINT16:
return SurfaceFormat::R5G6B5_UINT16;
case SurfaceFormat::A8:
return SurfaceFormat::A8;
default:
return SurfaceFormat::UNKNOWN;
}
}
inline SurfaceFormat ImageFormatToSurfaceFormat(gfxImageFormat aFormat)
{
switch (aFormat) {
case SurfaceFormat::A8R8G8B8_UINT32:
return SurfaceFormat::B8G8R8A8;
case SurfaceFormat::X8R8G8B8_UINT32:
return SurfaceFormat::B8G8R8X8;
case SurfaceFormat::R5G6B5_UINT16:
return SurfaceFormat::R5G6B5_UINT16;
case SurfaceFormat::A8:
return SurfaceFormat::A8;
default:
case SurfaceFormat::UNKNOWN:
return SurfaceFormat::B8G8R8A8;
}
}
inline gfxContentType ContentForFormat(const SurfaceFormat &aFormat)
{
switch (aFormat) {
case SurfaceFormat::R5G6B5_UINT16:
case SurfaceFormat::B8G8R8X8:
case SurfaceFormat::R8G8B8X8:
return gfxContentType::COLOR;
case SurfaceFormat::A8:
return gfxContentType::ALPHA;
case SurfaceFormat::B8G8R8A8:
case SurfaceFormat::R8G8B8A8:
default:
return gfxContentType::COLOR_ALPHA;
}
}
} // namespace gfx
} // namespace mozilla
#endif

620
gfx/thebes/gfxASurface.cpp Normal file
View file

@ -0,0 +1,620 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsIMemoryReporter.h"
#include "nsMemory.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Base64.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/Attributes.h"
#include "mozilla/MemoryReporting.h"
#include "nsISupportsImpl.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/Logging.h"
#include "mozilla/gfx/HelpersCairo.h"
#include "gfx2DGlue.h"
#include "gfxASurface.h"
#include "gfxContext.h"
#include "gfxImageSurface.h"
#include "gfxPlatform.h"
#include "gfxRect.h"
#include "cairo.h"
#include <algorithm>
#ifdef CAIRO_HAS_WIN32_SURFACE
#include "gfxWindowsSurface.h"
#endif
#ifdef MOZ_X11
#include "gfxXlibSurface.h"
#endif
#ifdef CAIRO_HAS_QUARTZ_SURFACE
#include "gfxQuartzSurface.h"
#endif
#include <stdio.h>
#include <limits.h>
#include "imgIEncoder.h"
#include "nsComponentManagerUtils.h"
#include "nsISupportsUtils.h"
#include "nsCOMPtr.h"
#include "nsServiceManagerUtils.h"
#include "nsString.h"
#include "nsIClipboardHelper.h"
using namespace mozilla;
using namespace mozilla::gfx;
static cairo_user_data_key_t gfxasurface_pointer_key;
gfxASurface::gfxASurface()
: mSurface(nullptr), mFloatingRefs(0), mBytesRecorded(0),
mSurfaceValid(false)
{
MOZ_COUNT_CTOR(gfxASurface);
}
gfxASurface::~gfxASurface()
{
RecordMemoryFreed();
MOZ_COUNT_DTOR(gfxASurface);
}
// Surfaces use refcounting that's tied to the cairo surface refcnt, to avoid
// refcount mismatch issues.
nsrefcnt
gfxASurface::AddRef(void)
{
if (mSurfaceValid) {
if (mFloatingRefs) {
// eat a floating ref
mFloatingRefs--;
} else {
cairo_surface_reference(mSurface);
}
return (nsrefcnt) cairo_surface_get_reference_count(mSurface);
} else {
// the surface isn't valid, but we still need to refcount
// the gfxASurface
return ++mFloatingRefs;
}
}
nsrefcnt
gfxASurface::Release(void)
{
if (mSurfaceValid) {
NS_ASSERTION(mFloatingRefs == 0, "gfxASurface::Release with floating refs still hanging around!");
// Note that there is a destructor set on user data for mSurface,
// which will delete this gfxASurface wrapper when the surface's refcount goes
// out of scope.
nsrefcnt refcnt = (nsrefcnt) cairo_surface_get_reference_count(mSurface);
cairo_surface_destroy(mSurface);
// |this| may not be valid any more, don't use it!
return --refcnt;
} else {
if (--mFloatingRefs == 0) {
delete this;
return 0;
}
return mFloatingRefs;
}
}
void
gfxASurface::SurfaceDestroyFunc(void *data) {
gfxASurface *surf = (gfxASurface*) data;
// fprintf (stderr, "Deleting wrapper for %p (wrapper: %p)\n", surf->mSurface, data);
delete surf;
}
gfxASurface*
gfxASurface::GetSurfaceWrapper(cairo_surface_t *csurf)
{
if (!csurf)
return nullptr;
return (gfxASurface*) cairo_surface_get_user_data(csurf, &gfxasurface_pointer_key);
}
void
gfxASurface::SetSurfaceWrapper(cairo_surface_t *csurf, gfxASurface *asurf)
{
if (!csurf)
return;
cairo_surface_set_user_data(csurf, &gfxasurface_pointer_key, asurf, SurfaceDestroyFunc);
}
already_AddRefed<gfxASurface>
gfxASurface::Wrap (cairo_surface_t *csurf, const IntSize& aSize)
{
RefPtr<gfxASurface> result;
/* Do we already have a wrapper for this surface? */
result = GetSurfaceWrapper(csurf);
if (result) {
// fprintf(stderr, "Existing wrapper for %p -> %p\n", csurf, result);
return result.forget();
}
/* No wrapper; figure out the surface type and create it */
cairo_surface_type_t stype = cairo_surface_get_type(csurf);
if (stype == CAIRO_SURFACE_TYPE_IMAGE) {
result = new gfxImageSurface(csurf);
}
#ifdef CAIRO_HAS_WIN32_SURFACE
else if (stype == CAIRO_SURFACE_TYPE_WIN32 ||
stype == CAIRO_SURFACE_TYPE_WIN32_PRINTING) {
result = new gfxWindowsSurface(csurf);
}
#endif
#ifdef MOZ_X11
else if (stype == CAIRO_SURFACE_TYPE_XLIB) {
result = new gfxXlibSurface(csurf);
}
#endif
#ifdef CAIRO_HAS_QUARTZ_SURFACE
else if (stype == CAIRO_SURFACE_TYPE_QUARTZ) {
result = new gfxQuartzSurface(csurf, aSize);
}
#endif
else {
result = new gfxUnknownSurface(csurf, aSize);
}
// fprintf(stderr, "New wrapper for %p -> %p\n", csurf, result);
return result.forget();
}
void
gfxASurface::Init(cairo_surface_t* surface, bool existingSurface)
{
SetSurfaceWrapper(surface, this);
MOZ_ASSERT(surface, "surface should be a valid pointer");
mSurface = surface;
mSurfaceValid = !cairo_surface_status(surface);
if (!mSurfaceValid) {
gfxWarning() << "ASurface Init failed with Cairo status " << cairo_surface_status(surface) << " on " << hexa(surface);
}
if (existingSurface || !mSurfaceValid) {
mFloatingRefs = 0;
} else {
mFloatingRefs = 1;
#ifdef MOZ_TREE_CAIRO
if (cairo_surface_get_content(surface) != CAIRO_CONTENT_COLOR) {
cairo_surface_set_subpixel_antialiasing(surface, CAIRO_SUBPIXEL_ANTIALIASING_DISABLED);
}
#endif
}
}
gfxSurfaceType
gfxASurface::GetType() const
{
if (!mSurfaceValid)
return (gfxSurfaceType)-1;
return (gfxSurfaceType)cairo_surface_get_type(mSurface);
}
gfxContentType
gfxASurface::GetContentType() const
{
if (!mSurfaceValid)
return (gfxContentType)-1;
return (gfxContentType)cairo_surface_get_content(mSurface);
}
void
gfxASurface::SetDeviceOffset(const gfxPoint& offset)
{
if (!mSurfaceValid)
return;
cairo_surface_set_device_offset(mSurface,
offset.x, offset.y);
}
gfxPoint
gfxASurface::GetDeviceOffset() const
{
if (!mSurfaceValid)
return gfxPoint(0.0, 0.0);
gfxPoint pt;
cairo_surface_get_device_offset(mSurface, &pt.x, &pt.y);
return pt;
}
void
gfxASurface::Flush() const
{
if (!mSurfaceValid)
return;
cairo_surface_flush(mSurface);
gfxPlatform::ClearSourceSurfaceForSurface(const_cast<gfxASurface*>(this));
}
void
gfxASurface::MarkDirty()
{
if (!mSurfaceValid)
return;
cairo_surface_mark_dirty(mSurface);
gfxPlatform::ClearSourceSurfaceForSurface(this);
}
void
gfxASurface::MarkDirty(const gfxRect& r)
{
if (!mSurfaceValid)
return;
cairo_surface_mark_dirty_rectangle(mSurface,
(int) r.X(), (int) r.Y(),
(int) r.Width(), (int) r.Height());
gfxPlatform::ClearSourceSurfaceForSurface(this);
}
void
gfxASurface::SetData(const cairo_user_data_key_t *key,
void *user_data,
thebes_destroy_func_t destroy)
{
if (!mSurfaceValid)
return;
cairo_surface_set_user_data(mSurface, key, user_data, destroy);
}
void *
gfxASurface::GetData(const cairo_user_data_key_t *key)
{
if (!mSurfaceValid)
return nullptr;
return cairo_surface_get_user_data(mSurface, key);
}
void
gfxASurface::Finish()
{
// null surfaces are allowed here
cairo_surface_finish(mSurface);
}
already_AddRefed<gfxASurface>
gfxASurface::CreateSimilarSurface(gfxContentType aContent,
const IntSize& aSize)
{
if (!mSurface || !mSurfaceValid) {
return nullptr;
}
cairo_surface_t *surface =
cairo_surface_create_similar(mSurface, cairo_content_t(int(aContent)),
aSize.width, aSize.height);
if (cairo_surface_status(surface)) {
cairo_surface_destroy(surface);
return nullptr;
}
RefPtr<gfxASurface> result = Wrap(surface, aSize);
cairo_surface_destroy(surface);
return result.forget();
}
already_AddRefed<gfxImageSurface>
gfxASurface::CopyToARGB32ImageSurface()
{
if (!mSurface || !mSurfaceValid) {
return nullptr;
}
const IntSize size = GetSize();
RefPtr<gfxImageSurface> imgSurface =
new gfxImageSurface(size, SurfaceFormat::A8R8G8B8_UINT32);
RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(imgSurface, IntSize(size.width, size.height));
RefPtr<SourceSurface> source = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, this);
dt->CopySurface(source, IntRect(0, 0, size.width, size.height), IntPoint());
return imgSurface.forget();
}
int
gfxASurface::CairoStatus()
{
if (!mSurfaceValid)
return -1;
return cairo_surface_status(mSurface);
}
/* static */
int32_t
gfxASurface::FormatStrideForWidth(gfxImageFormat format, int32_t width)
{
cairo_format_t cformat = GfxFormatToCairoFormat(format);
return cairo_format_stride_for_width(cformat, (int)width);
}
nsresult
gfxASurface::BeginPrinting(const nsAString& aTitle, const nsAString& aPrintToFileName)
{
return NS_OK;
}
nsresult
gfxASurface::EndPrinting()
{
return NS_OK;
}
nsresult
gfxASurface::AbortPrinting()
{
return NS_OK;
}
nsresult
gfxASurface::BeginPage()
{
return NS_OK;
}
nsresult
gfxASurface::EndPage()
{
return NS_OK;
}
gfxContentType
gfxASurface::ContentFromFormat(gfxImageFormat format)
{
switch (format) {
case SurfaceFormat::A8R8G8B8_UINT32:
return gfxContentType::COLOR_ALPHA;
case SurfaceFormat::X8R8G8B8_UINT32:
case SurfaceFormat::R5G6B5_UINT16:
return gfxContentType::COLOR;
case SurfaceFormat::A8:
return gfxContentType::ALPHA;
case SurfaceFormat::UNKNOWN:
default:
return gfxContentType::COLOR;
}
}
int32_t
gfxASurface::BytePerPixelFromFormat(gfxImageFormat format)
{
switch (format) {
case SurfaceFormat::A8R8G8B8_UINT32:
case SurfaceFormat::X8R8G8B8_UINT32:
return 4;
case SurfaceFormat::R5G6B5_UINT16:
return 2;
case SurfaceFormat::A8:
return 1;
default:
NS_WARNING("Unknown byte per pixel value for Image format");
}
return 0;
}
/** Memory reporting **/
static const char *sDefaultSurfaceDescription =
"Memory used by gfx surface of the given type.";
struct SurfaceMemoryReporterAttrs {
const char *path;
const char *description;
};
static const SurfaceMemoryReporterAttrs sSurfaceMemoryReporterAttrs[] = {
{"gfx-surface-image", nullptr},
{"gfx-surface-pdf", nullptr},
{"gfx-surface-ps", nullptr},
{"gfx-surface-xlib",
"Memory used by xlib surfaces to store pixmaps. This memory lives in "
"the X server's process rather than in this application, so the bytes "
"accounted for here aren't counted in vsize, resident, explicit, or any of "
"the other measurements on this page."},
{"gfx-surface-xcb", nullptr},
{"gfx-surface-glitz???", nullptr}, // should never be used
{"gfx-surface-quartz", nullptr},
{"gfx-surface-win32", nullptr},
{"gfx-surface-beos", nullptr},
{"gfx-surface-directfb???", nullptr}, // should never be used
{"gfx-surface-svg", nullptr},
{"gfx-surface-os2", nullptr},
{"gfx-surface-win32printing", nullptr},
{"gfx-surface-quartzimage", nullptr},
{"gfx-surface-script", nullptr},
{"gfx-surface-qpainter", nullptr},
{"gfx-surface-recording", nullptr},
{"gfx-surface-vg", nullptr},
{"gfx-surface-gl", nullptr},
{"gfx-surface-drm", nullptr},
{"gfx-surface-tee", nullptr},
{"gfx-surface-xml", nullptr},
{"gfx-surface-skia", nullptr},
{"gfx-surface-subsurface", nullptr},
};
static_assert(MOZ_ARRAY_LENGTH(sSurfaceMemoryReporterAttrs) ==
size_t(gfxSurfaceType::Max), "sSurfaceMemoryReporterAttrs exceeds max capacity");
static_assert(uint32_t(CAIRO_SURFACE_TYPE_SKIA) ==
uint32_t(gfxSurfaceType::Skia), "CAIRO_SURFACE_TYPE_SKIA not equal to gfxSurfaceType::Skia");
/* Surface size memory reporting */
class SurfaceMemoryReporter final : public nsIMemoryReporter
{
~SurfaceMemoryReporter() {}
// We can touch this array on several different threads, and we don't
// want to introduce memory barriers when recording the memory used. To
// assure dynamic race checkers like TSan that this is OK, we use
// relaxed memory ordering here.
static Atomic<size_t, Relaxed> sSurfaceMemoryUsed[size_t(gfxSurfaceType::Max)];
public:
static void AdjustUsedMemory(gfxSurfaceType aType, int32_t aBytes)
{
// A read-modify-write operation like += would require a memory barrier
// here, which would defeat the purpose of using relaxed memory
// ordering. So separate out the read and write operations.
sSurfaceMemoryUsed[size_t(aType)] = sSurfaceMemoryUsed[size_t(aType)] + aBytes;
};
NS_DECL_ISUPPORTS
NS_IMETHOD CollectReports(nsIHandleReportCallback *aHandleReport,
nsISupports *aData, bool aAnonymize) override
{
const size_t len = ArrayLength(sSurfaceMemoryReporterAttrs);
for (size_t i = 0; i < len; i++) {
int64_t amount = sSurfaceMemoryUsed[i];
if (amount != 0) {
const char *path = sSurfaceMemoryReporterAttrs[i].path;
const char *desc = sSurfaceMemoryReporterAttrs[i].description;
if (!desc) {
desc = sDefaultSurfaceDescription;
}
aHandleReport->Callback(
EmptyCString(), nsCString(path), KIND_OTHER, UNITS_BYTES,
amount, nsCString(desc), aData);
}
}
return NS_OK;
}
};
Atomic<size_t, Relaxed> SurfaceMemoryReporter::sSurfaceMemoryUsed[size_t(gfxSurfaceType::Max)];
NS_IMPL_ISUPPORTS(SurfaceMemoryReporter, nsIMemoryReporter)
void
gfxASurface::RecordMemoryUsedForSurfaceType(gfxSurfaceType aType,
int32_t aBytes)
{
if (int(aType) < 0 || aType >= gfxSurfaceType::Max) {
NS_WARNING("Invalid type to RecordMemoryUsedForSurfaceType!");
return;
}
static bool registered = false;
if (!registered) {
RegisterStrongMemoryReporter(new SurfaceMemoryReporter());
registered = true;
}
SurfaceMemoryReporter::AdjustUsedMemory(aType, aBytes);
}
void
gfxASurface::RecordMemoryUsed(int32_t aBytes)
{
RecordMemoryUsedForSurfaceType(GetType(), aBytes);
mBytesRecorded += aBytes;
}
void
gfxASurface::RecordMemoryFreed()
{
if (mBytesRecorded) {
RecordMemoryUsedForSurfaceType(GetType(), -mBytesRecorded);
mBytesRecorded = 0;
}
}
size_t
gfxASurface::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
{
// We don't measure mSurface because cairo doesn't allow it.
return 0;
}
size_t
gfxASurface::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
{
return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
}
/* static */ uint8_t
gfxASurface::BytesPerPixel(gfxImageFormat aImageFormat)
{
switch (aImageFormat) {
case SurfaceFormat::A8R8G8B8_UINT32:
return 4;
case SurfaceFormat::X8R8G8B8_UINT32:
return 4;
case SurfaceFormat::R5G6B5_UINT16:
return 2;
case SurfaceFormat::A8:
return 1;
case SurfaceFormat::UNKNOWN:
default:
NS_NOTREACHED("Not really sure what you want me to say here");
return 0;
}
}
void
gfxASurface::SetOpaqueRect(const gfxRect& aRect)
{
if (aRect.IsEmpty()) {
mOpaqueRect = nullptr;
} else if (!!mOpaqueRect) {
*mOpaqueRect = aRect;
} else {
mOpaqueRect = MakeUnique<gfxRect>(aRect);
}
}
/* static */const gfxRect&
gfxASurface::GetEmptyOpaqueRect()
{
static const gfxRect empty(0, 0, 0, 0);
return empty;
}
const IntSize
gfxASurface::GetSize() const
{
return IntSize(-1, -1);
}
SurfaceFormat
gfxASurface::GetSurfaceFormat() const
{
if (!mSurfaceValid) {
return SurfaceFormat::UNKNOWN;
}
return GfxFormatForCairoSurface(mSurface);
}
already_AddRefed<gfxImageSurface>
gfxASurface::GetAsImageSurface()
{
return nullptr;
}

204
gfx/thebes/gfxASurface.h Normal file
View file

@ -0,0 +1,204 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_ASURFACE_H
#define GFX_ASURFACE_H
#include "mozilla/MemoryReporting.h"
#include "mozilla/UniquePtr.h"
#include "gfxTypes.h"
#include "nscore.h"
#include "nsSize.h"
#include "mozilla/gfx/Rect.h"
#ifdef MOZILLA_INTERNAL_API
#include "nsStringFwd.h"
#else
#include "nsStringAPI.h"
#endif
class gfxImageSurface;
struct gfxRect;
struct gfxPoint;
template <typename T>
struct already_AddRefed;
/**
* A surface is something you can draw on. Instantiate a subclass of this
* abstract class, and use gfxContext to draw on this surface.
*/
class gfxASurface {
public:
#ifdef MOZILLA_INTERNAL_API
nsrefcnt AddRef(void);
nsrefcnt Release(void);
#else
virtual nsrefcnt AddRef(void);
virtual nsrefcnt Release(void);
#endif
public:
/** Wrap the given cairo surface and return a gfxASurface for it.
* This adds a reference to csurf (owned by the returned gfxASurface).
*/
static already_AddRefed<gfxASurface> Wrap(cairo_surface_t *csurf, const mozilla::gfx::IntSize& aSize = mozilla::gfx::IntSize(-1, -1));
/*** this DOES NOT addref the surface */
cairo_surface_t *CairoSurface() {
return mSurface;
}
gfxSurfaceType GetType() const;
gfxContentType GetContentType() const;
void SetDeviceOffset(const gfxPoint& offset);
gfxPoint GetDeviceOffset() const;
void Flush() const;
void MarkDirty();
void MarkDirty(const gfxRect& r);
/* Printing backend functions */
virtual nsresult BeginPrinting(const nsAString& aTitle, const nsAString& aPrintToFileName);
virtual nsresult EndPrinting();
virtual nsresult AbortPrinting();
virtual nsresult BeginPage();
virtual nsresult EndPage();
void SetData(const cairo_user_data_key_t *key,
void *user_data,
thebes_destroy_func_t destroy);
void *GetData(const cairo_user_data_key_t *key);
virtual void Finish();
/**
* Create an offscreen surface that can be efficiently copied into
* this surface (at least if tiling is not involved).
* Returns null on error.
*/
virtual already_AddRefed<gfxASurface> CreateSimilarSurface(gfxContentType aType,
const mozilla::gfx::IntSize& aSize);
/**
* Returns an image surface for this surface, or nullptr if not supported.
* This will not copy image data, just wraps an image surface around
* pixel data already available in memory.
*/
virtual already_AddRefed<gfxImageSurface> GetAsImageSurface();
/**
* Creates a new ARGB32 image surface with the same contents as this surface.
* Returns null on error.
*/
already_AddRefed<gfxImageSurface> CopyToARGB32ImageSurface();
int CairoStatus();
/* Provide a stride value that will respect all alignment requirements of
* the accelerated image-rendering code.
*/
static int32_t FormatStrideForWidth(gfxImageFormat format, int32_t width);
static gfxContentType ContentFromFormat(gfxImageFormat format);
/**
* Record number of bytes for given surface type. Use positive bytes
* for allocations and negative bytes for deallocations.
*/
static void RecordMemoryUsedForSurfaceType(gfxSurfaceType aType,
int32_t aBytes);
/**
* Same as above, but use current surface type as returned by GetType().
* The bytes will be accumulated until RecordMemoryFreed is called,
* in which case the value that was recorded for this surface will
* be freed.
*/
void RecordMemoryUsed(int32_t aBytes);
void RecordMemoryFreed();
virtual int32_t KnownMemoryUsed() { return mBytesRecorded; }
virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
// gfxASurface has many sub-classes. This method indicates if a sub-class
// is capable of measuring its own size accurately. If not, the caller
// must fall back to a computed size. (Note that gfxASurface can actually
// measure itself, but we must |return false| here because it serves as the
// (conservative) default for all the sub-classes. Therefore, this
// function should only be called on a |gfxASurface*| that actually points
// to a sub-class of gfxASurface.)
virtual bool SizeOfIsMeasured() const { return false; }
static int32_t BytePerPixelFromFormat(gfxImageFormat format);
virtual const mozilla::gfx::IntSize GetSize() const;
virtual mozilla::gfx::SurfaceFormat GetSurfaceFormat() const;
void SetOpaqueRect(const gfxRect& aRect);
const gfxRect& GetOpaqueRect() {
if (!!mOpaqueRect)
return *mOpaqueRect;
return GetEmptyOpaqueRect();
}
static uint8_t BytesPerPixel(gfxImageFormat aImageFormat);
protected:
gfxASurface();
static gfxASurface* GetSurfaceWrapper(cairo_surface_t *csurf);
static void SetSurfaceWrapper(cairo_surface_t *csurf, gfxASurface *asurf);
// NB: Init() *must* be called from within subclass's
// constructors. It's unsafe to call it after the ctor finishes;
// leaks and use-after-frees are possible.
void Init(cairo_surface_t *surface, bool existingSurface = false);
// out-of-line helper to allow GetOpaqueRect() to be inlined
// without including gfxRect.h here
static const gfxRect& GetEmptyOpaqueRect();
virtual ~gfxASurface();
cairo_surface_t *mSurface;
mozilla::UniquePtr<gfxRect> mOpaqueRect;
private:
static void SurfaceDestroyFunc(void *data);
int32_t mFloatingRefs;
int32_t mBytesRecorded;
protected:
bool mSurfaceValid;
};
/**
* An Unknown surface; used to wrap unknown cairo_surface_t returns from cairo
*/
class gfxUnknownSurface : public gfxASurface {
public:
gfxUnknownSurface(cairo_surface_t *surf, const mozilla::gfx::IntSize& aSize)
: mSize(aSize)
{
Init(surf, true);
}
virtual ~gfxUnknownSurface() { }
virtual const mozilla::gfx::IntSize GetSize() const override { return mSize; }
private:
mozilla::gfx::IntSize mSize;
};
#endif /* GFX_ASURFACE_H */

View file

@ -0,0 +1,53 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gfxAlphaRecovery.h"
#include "gfxImageSurface.h"
#define MOZILLA_SSE_INCLUDE_HEADER_FOR_SSE2
#include "mozilla/SSE.h"
/* static */ bool
gfxAlphaRecovery::RecoverAlpha(gfxImageSurface* blackSurf,
const gfxImageSurface* whiteSurf)
{
mozilla::gfx::IntSize size = blackSurf->GetSize();
if (size != whiteSurf->GetSize() ||
(blackSurf->Format() != mozilla::gfx::SurfaceFormat::A8R8G8B8_UINT32 &&
blackSurf->Format() != mozilla::gfx::SurfaceFormat::X8R8G8B8_UINT32) ||
(whiteSurf->Format() != mozilla::gfx::SurfaceFormat::A8R8G8B8_UINT32 &&
whiteSurf->Format() != mozilla::gfx::SurfaceFormat::X8R8G8B8_UINT32))
return false;
#ifdef MOZILLA_MAY_SUPPORT_SSE2
if (mozilla::supports_sse2() &&
RecoverAlphaSSE2(blackSurf, whiteSurf)) {
return true;
}
#endif
blackSurf->Flush();
whiteSurf->Flush();
unsigned char* blackData = blackSurf->Data();
unsigned char* whiteData = whiteSurf->Data();
for (int32_t i = 0; i < size.height; ++i) {
uint32_t* blackPixel = reinterpret_cast<uint32_t*>(blackData);
const uint32_t* whitePixel = reinterpret_cast<uint32_t*>(whiteData);
for (int32_t j = 0; j < size.width; ++j) {
uint32_t recovered = RecoverPixel(blackPixel[j], whitePixel[j]);
blackPixel[j] = recovered;
}
blackData += blackSurf->Stride();
whiteData += whiteSurf->Stride();
}
blackSurf->MarkDirty();
return true;
}

View file

@ -0,0 +1,111 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef _GFXALPHARECOVERY_H_
#define _GFXALPHARECOVERY_H_
#include "mozilla/SSE.h"
#include "gfxTypes.h"
#include "mozilla/gfx/Rect.h"
class gfxImageSurface;
class gfxAlphaRecovery {
public:
/**
* Some SIMD fast-paths only can be taken if the relative
* byte-alignment of images' pointers and strides meets certain
* criteria. Aligning image pointers and strides by
* |GoodAlignmentLog2()| below will ensure that fast-paths aren't
* skipped because of misalignment. Fast-paths may still be taken
* even if GoodAlignmentLog2() is not met, in some conditions.
*/
static uint32_t GoodAlignmentLog2() { return 4; /* for SSE2 */ }
/* Given two surfaces of equal size with the same rendering, one onto a
* black background and the other onto white, recovers alpha values from
* the difference and sets the alpha values on the black surface.
* The surfaces must have format RGB24 or ARGB32.
* Returns true on success.
*/
static bool RecoverAlpha (gfxImageSurface *blackSurface,
const gfxImageSurface *whiteSurface);
#ifdef MOZILLA_MAY_SUPPORT_SSE2
/* This does the same as the previous function, but uses SSE2
* optimizations. Usually this should not be called directly. Be sure to
* check mozilla::supports_sse2() before calling this function.
*/
static bool RecoverAlphaSSE2 (gfxImageSurface *blackSurface,
const gfxImageSurface *whiteSurface);
/**
* A common use-case for alpha recovery is to paint into a
* temporary "white image", then paint onto a subrect of the
* surface, the "black image", into which alpha-recovered pixels
* are eventually to be written. This function returns a rect
* aligned so that recovering alpha for that rect will hit SIMD
* fast-paths, if possible. It's not always possible to align
* |aRect| so that fast-paths will be taken.
*
* The returned rect is always a superset of |aRect|.
*/
static mozilla::gfx::IntRect AlignRectForSubimageRecovery(const mozilla::gfx::IntRect& aRect,
gfxImageSurface* aSurface);
#else
static mozilla::gfx::IntRect AlignRectForSubimageRecovery(const mozilla::gfx::IntRect& aRect,
gfxImageSurface*)
{ return aRect; }
#endif
/** from cairo-xlib-utils.c, modified */
/**
* Given the RGB data for two image surfaces, one a source image composited
* with OVER onto a black background, and one a source image composited with
* OVER onto a white background, reconstruct the original image data into
* black_data.
*
* Consider a single color channel and a given pixel. Suppose the original
* premultiplied color value was C and the alpha value was A. Let the final
* on-black color be B and the final on-white color be W. All values range
* over 0-255.
*
* Then B=C and W=(255*(255 - A) + C*255)/255. Solving for A, we get
* A=255 - (W - C). Therefore it suffices to leave the black_data color
* data alone and set the alpha values using that simple formula. It shouldn't
* matter what color channel we pick for the alpha computation, but we'll
* pick green because if we went through a color channel downsample the green
* bits are likely to be the most accurate.
*
* This function needs to be in the header file since it's used by both
* gfxRecoverAlpha.cpp and gfxRecoverAlphaSSE2.cpp.
*/
static inline uint32_t
RecoverPixel(uint32_t black, uint32_t white)
{
const uint32_t GREEN_MASK = 0x0000FF00;
const uint32_t ALPHA_MASK = 0xFF000000;
/* |diff| here is larger when the source image pixel is more transparent.
If both renderings are from the same source image composited with OVER,
then the color values on white will always be greater than those on
black, so |diff| would not overflow. However, overflow may happen, for
example, when a plugin plays a video and the image is rapidly changing.
If there is overflow, then behave as if we limit to the difference to
>= 0, which will make the rendering opaque. (Without this overflow
will make the rendering transparent.) */
uint32_t diff = (white & GREEN_MASK) - (black & GREEN_MASK);
/* |diff| is 0xFFFFxx00 on overflow and 0x0000xx00 otherwise, so use this
to limit the transparency. */
uint32_t limit = diff & ALPHA_MASK;
/* The alpha bits of the result */
uint32_t alpha = (ALPHA_MASK - (diff << 16)) | limit;
return alpha | (black & ~ALPHA_MASK);
}
};
#endif /* _GFXALPHARECOVERY_H_ */

View file

@ -0,0 +1,235 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gfxAlphaRecovery.h"
#include "gfxImageSurface.h"
#include <emmintrin.h>
// This file should only be compiled on x86 and x64 systems. Additionally,
// you'll need to compile it with -msse2 if you're using GCC on x86.
#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_AMD64))
__declspec(align(16)) static uint32_t greenMaski[] =
{ 0x0000ff00, 0x0000ff00, 0x0000ff00, 0x0000ff00 };
__declspec(align(16)) static uint32_t alphaMaski[] =
{ 0xff000000, 0xff000000, 0xff000000, 0xff000000 };
#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
static uint32_t greenMaski[] __attribute__ ((aligned (16))) =
{ 0x0000ff00, 0x0000ff00, 0x0000ff00, 0x0000ff00 };
static uint32_t alphaMaski[] __attribute__ ((aligned (16))) =
{ 0xff000000, 0xff000000, 0xff000000, 0xff000000 };
#elif defined(__SUNPRO_CC) && (defined(__i386) || defined(__x86_64__))
#pragma align 16 (greenMaski, alphaMaski)
static uint32_t greenMaski[] = { 0x0000ff00, 0x0000ff00, 0x0000ff00, 0x0000ff00 };
static uint32_t alphaMaski[] = { 0xff000000, 0xff000000, 0xff000000, 0xff000000 };
#endif
bool
gfxAlphaRecovery::RecoverAlphaSSE2(gfxImageSurface* blackSurf,
const gfxImageSurface* whiteSurf)
{
mozilla::gfx::IntSize size = blackSurf->GetSize();
if (size != whiteSurf->GetSize() ||
(blackSurf->Format() != mozilla::gfx::SurfaceFormat::A8R8G8B8_UINT32 &&
blackSurf->Format() != mozilla::gfx::SurfaceFormat::X8R8G8B8_UINT32) ||
(whiteSurf->Format() != mozilla::gfx::SurfaceFormat::A8R8G8B8_UINT32 &&
whiteSurf->Format() != mozilla::gfx::SurfaceFormat::X8R8G8B8_UINT32))
return false;
blackSurf->Flush();
whiteSurf->Flush();
unsigned char* blackData = blackSurf->Data();
unsigned char* whiteData = whiteSurf->Data();
if ((NS_PTR_TO_UINT32(blackData) & 0xf) != (NS_PTR_TO_UINT32(whiteData) & 0xf) ||
(blackSurf->Stride() - whiteSurf->Stride()) & 0xf) {
// Cannot keep these in alignment.
return false;
}
__m128i greenMask = _mm_load_si128((__m128i*)greenMaski);
__m128i alphaMask = _mm_load_si128((__m128i*)alphaMaski);
for (int32_t i = 0; i < size.height; ++i) {
int32_t j = 0;
// Loop single pixels until at 4 byte alignment.
while (NS_PTR_TO_UINT32(blackData) & 0xf && j < size.width) {
*((uint32_t*)blackData) =
RecoverPixel(*reinterpret_cast<uint32_t*>(blackData),
*reinterpret_cast<uint32_t*>(whiteData));
blackData += 4;
whiteData += 4;
j++;
}
// This extra loop allows the compiler to do some more clever registry
// management and makes it about 5% faster than with only the 4 pixel
// at a time loop.
for (; j < size.width - 8; j += 8) {
__m128i black1 = _mm_load_si128((__m128i*)blackData);
__m128i white1 = _mm_load_si128((__m128i*)whiteData);
__m128i black2 = _mm_load_si128((__m128i*)(blackData + 16));
__m128i white2 = _mm_load_si128((__m128i*)(whiteData + 16));
// Execute the same instructions as described in RecoverPixel, only
// using an SSE2 packed saturated subtract.
white1 = _mm_subs_epu8(white1, black1);
white2 = _mm_subs_epu8(white2, black2);
white1 = _mm_subs_epu8(greenMask, white1);
white2 = _mm_subs_epu8(greenMask, white2);
// Producing the final black pixel in an XMM register and storing
// that is actually faster than doing a masked store since that
// does an unaligned storage. We have the black pixel in a register
// anyway.
black1 = _mm_andnot_si128(alphaMask, black1);
black2 = _mm_andnot_si128(alphaMask, black2);
white1 = _mm_slli_si128(white1, 2);
white2 = _mm_slli_si128(white2, 2);
white1 = _mm_and_si128(alphaMask, white1);
white2 = _mm_and_si128(alphaMask, white2);
black1 = _mm_or_si128(white1, black1);
black2 = _mm_or_si128(white2, black2);
_mm_store_si128((__m128i*)blackData, black1);
_mm_store_si128((__m128i*)(blackData + 16), black2);
blackData += 32;
whiteData += 32;
}
for (; j < size.width - 4; j += 4) {
__m128i black = _mm_load_si128((__m128i*)blackData);
__m128i white = _mm_load_si128((__m128i*)whiteData);
white = _mm_subs_epu8(white, black);
white = _mm_subs_epu8(greenMask, white);
black = _mm_andnot_si128(alphaMask, black);
white = _mm_slli_si128(white, 2);
white = _mm_and_si128(alphaMask, white);
black = _mm_or_si128(white, black);
_mm_store_si128((__m128i*)blackData, black);
blackData += 16;
whiteData += 16;
}
// Loop single pixels until we're done.
while (j < size.width) {
*((uint32_t*)blackData) =
RecoverPixel(*reinterpret_cast<uint32_t*>(blackData),
*reinterpret_cast<uint32_t*>(whiteData));
blackData += 4;
whiteData += 4;
j++;
}
blackData += blackSurf->Stride() - j * 4;
whiteData += whiteSurf->Stride() - j * 4;
}
blackSurf->MarkDirty();
return true;
}
static int32_t
ByteAlignment(int32_t aAlignToLog2, int32_t aX, int32_t aY=0, int32_t aStride=1)
{
return (aX + aStride * aY) & ((1 << aAlignToLog2) - 1);
}
/*static*/ mozilla::gfx::IntRect
gfxAlphaRecovery::AlignRectForSubimageRecovery(const mozilla::gfx::IntRect& aRect,
gfxImageSurface* aSurface)
{
NS_ASSERTION(mozilla::gfx::SurfaceFormat::A8R8G8B8_UINT32 == aSurface->Format(),
"Thebes grew support for non-ARGB32 COLOR_ALPHA?");
static const int32_t kByteAlignLog2 = GoodAlignmentLog2();
static const int32_t bpp = 4;
static const int32_t pixPerAlign = (1 << kByteAlignLog2) / bpp;
//
// We're going to create a subimage of the surface with size
// <sw,sh> for alpha recovery, and want a SIMD fast-path. The
// rect <x,y, w,h> /needs/ to be redrawn, but it might not be
// properly aligned for SIMD. So we want to find a rect <x',y',
// w',h'> that's a superset of what needs to be redrawn but is
// properly aligned. Proper alignment is
//
// BPP * (x' + y' * sw) \cong 0 (mod ALIGN)
// BPP * w' \cong BPP * sw (mod ALIGN)
//
// (We assume the pixel at surface <0,0> is already ALIGN'd.)
// That rect (obviously) has to fit within the surface bounds, and
// we should also minimize the extra pixels redrawn only for
// alignment's sake. So we also want
//
// minimize <x',y', w',h'>
// 0 <= x' <= x
// 0 <= y' <= y
// w <= w' <= sw
// h <= h' <= sh
//
// This is a messy integer non-linear programming problem, except
// ... we can assume that ALIGN/BPP is a very small constant. So,
// brute force is viable. The algorithm below will find a
// solution if one exists, but isn't guaranteed to find the
// minimum solution. (For SSE2, ALIGN/BPP = 4, so it'll do at
// most 64 iterations below). In what's likely the common case,
// an already-aligned rectangle, it only needs 1 iteration.
//
// Is this alignment worth doing? Recovering alpha will take work
// proportional to w*h (assuming alpha recovery computation isn't
// memory bound). This analysis can lead to O(w+h) extra work
// (with small constants). In exchange, we expect to shave off a
// ALIGN/BPP constant by using SIMD-ized alpha recovery. So as
// w*h diverges from w+h, the win factor approaches ALIGN/BPP. We
// only really care about the w*h >> w+h case anyway; others
// should be fast enough even with the overhead. (Unless the cost
// of repainting the expanded rect is high, but in that case
// SIMD-ized alpha recovery won't make a difference so this code
// shouldn't be called.)
//
mozilla::gfx::IntSize surfaceSize = aSurface->GetSize();
const int32_t stride = bpp * surfaceSize.width;
if (stride != aSurface->Stride()) {
NS_WARNING("Unexpected stride, falling back on slow alpha recovery");
return aRect;
}
const int32_t x = aRect.x, y = aRect.y, w = aRect.width, h = aRect.height;
const int32_t r = x + w;
const int32_t sw = surfaceSize.width;
const int32_t strideAlign = ByteAlignment(kByteAlignLog2, stride);
// The outer two loops below keep the rightmost (|r| above) and
// bottommost pixels in |aRect| fixed wrt <x,y>, to ensure that we
// return only a superset of the original rect. These loops
// search for an aligned top-left pixel by trying to expand <x,y>
// left and up by <dx,dy> pixels, respectively.
//
// Then if a properly-aligned top-left pixel is found, the
// innermost loop tries to find an aligned stride by moving the
// rightmost pixel rightward by dr.
int32_t dx, dy, dr;
for (dy = 0; (dy < pixPerAlign) && (y - dy >= 0); ++dy) {
for (dx = 0; (dx < pixPerAlign) && (x - dx >= 0); ++dx) {
if (0 != ByteAlignment(kByteAlignLog2,
bpp * (x - dx), y - dy, stride)) {
continue;
}
for (dr = 0; (dr < pixPerAlign) && (r + dr <= sw); ++dr) {
if (strideAlign == ByteAlignment(kByteAlignLog2,
bpp * (w + dr + dx))) {
goto FOUND_SOLUTION;
}
}
}
}
// Didn't find a solution.
return aRect;
FOUND_SOLUTION:
mozilla::gfx::IntRect solution = mozilla::gfx::IntRect(x - dx, y - dy, w + dr + dx, h + dy);
MOZ_ASSERT(mozilla::gfx::IntRect(0, 0, sw, surfaceSize.height).Contains(solution),
"'Solution' extends outside surface bounds!");
return solution;
}

View file

@ -0,0 +1,338 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "base/basictypes.h"
#include "gfxAndroidPlatform.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/CountingAllocatorBase.h"
#include "mozilla/Preferences.h"
#include "gfx2DGlue.h"
#include "gfxFT2FontList.h"
#include "gfxImageSurface.h"
#include "gfxTextRun.h"
#include "mozilla/dom/ContentChild.h"
#include "nsXULAppAPI.h"
#include "nsIScreen.h"
#include "nsIScreenManager.h"
#include "nsILocaleService.h"
#include "nsServiceManagerUtils.h"
#include "gfxPrefs.h"
#include "cairo.h"
#include "VsyncSource.h"
#include "ft2build.h"
#include FT_FREETYPE_H
#include FT_MODULE_H
using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::gfx;
static FT_Library gPlatformFTLibrary = nullptr;
class FreetypeReporter final : public nsIMemoryReporter,
public CountingAllocatorBase<FreetypeReporter>
{
private:
~FreetypeReporter() {}
public:
NS_DECL_ISUPPORTS
static void* Malloc(FT_Memory, long size)
{
return CountingMalloc(size);
}
static void Free(FT_Memory, void* p)
{
return CountingFree(p);
}
static void*
Realloc(FT_Memory, long cur_size, long new_size, void* p)
{
return CountingRealloc(p, new_size);
}
NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
nsISupports* aData, bool aAnonymize) override
{
MOZ_COLLECT_REPORT(
"explicit/freetype", KIND_HEAP, UNITS_BYTES, MemoryAllocated(),
"Memory used by Freetype.");
return NS_OK;
}
};
NS_IMPL_ISUPPORTS(FreetypeReporter, nsIMemoryReporter)
template<> Atomic<size_t> CountingAllocatorBase<FreetypeReporter>::sAmount(0);
static FT_MemoryRec_ sFreetypeMemoryRecord;
gfxAndroidPlatform::gfxAndroidPlatform()
{
// A custom allocator. It counts allocations, enabling memory reporting.
sFreetypeMemoryRecord.user = nullptr;
sFreetypeMemoryRecord.alloc = FreetypeReporter::Malloc;
sFreetypeMemoryRecord.free = FreetypeReporter::Free;
sFreetypeMemoryRecord.realloc = FreetypeReporter::Realloc;
// These two calls are equivalent to FT_Init_FreeType(), but allow us to
// provide a custom memory allocator.
FT_New_Library(&sFreetypeMemoryRecord, &gPlatformFTLibrary);
FT_Add_Default_Modules(gPlatformFTLibrary);
RegisterStrongMemoryReporter(new FreetypeReporter());
mOffscreenFormat = GetScreenDepth() == 16
? SurfaceFormat::R5G6B5_UINT16
: SurfaceFormat::X8R8G8B8_UINT32;
if (gfxPrefs::AndroidRGB16Force()) {
mOffscreenFormat = SurfaceFormat::R5G6B5_UINT16;
}
}
gfxAndroidPlatform::~gfxAndroidPlatform()
{
FT_Done_Library(gPlatformFTLibrary);
gPlatformFTLibrary = nullptr;
}
already_AddRefed<gfxASurface>
gfxAndroidPlatform::CreateOffscreenSurface(const IntSize& aSize,
gfxImageFormat aFormat)
{
if (!Factory::AllowedSurfaceSize(aSize)) {
return nullptr;
}
RefPtr<gfxASurface> newSurface;
newSurface = new gfxImageSurface(aSize, aFormat);
return newSurface.forget();
}
static bool
IsJapaneseLocale()
{
static bool sInitialized = false;
static bool sIsJapanese = false;
if (!sInitialized) {
sInitialized = true;
do { // to allow 'break' to abandon this block if a call fails
nsresult rv;
nsCOMPtr<nsILocaleService> ls =
do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv);
if (NS_FAILED(rv)) {
break;
}
nsCOMPtr<nsILocale> appLocale;
rv = ls->GetApplicationLocale(getter_AddRefs(appLocale));
if (NS_FAILED(rv)) {
break;
}
nsString localeStr;
rv = appLocale->
GetCategory(NS_LITERAL_STRING(NSILOCALE_MESSAGE), localeStr);
if (NS_FAILED(rv)) {
break;
}
const nsAString& lang = nsDependentSubstring(localeStr, 0, 2);
if (lang.EqualsLiteral("ja")) {
sIsJapanese = true;
}
} while (false);
}
return sIsJapanese;
}
void
gfxAndroidPlatform::GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh,
Script aRunScript,
nsTArray<const char*>& aFontList)
{
static const char kDroidSansJapanese[] = "Droid Sans Japanese";
static const char kMotoyaLMaru[] = "MotoyaLMaru";
static const char kNotoSansCJKJP[] = "Noto Sans CJK JP";
static const char kNotoColorEmoji[] = "Noto Color Emoji";
if (aNextCh == 0xfe0fu) {
// if char is followed by VS16, try for a color emoji glyph
aFontList.AppendElement(kNotoColorEmoji);
}
if (!IS_IN_BMP(aCh)) {
uint32_t p = aCh >> 16;
if (p == 1) { // try color emoji font, unless VS15 (text style) present
if (aNextCh != 0xfe0fu && aNextCh != 0xfe0eu) {
aFontList.AppendElement(kNotoColorEmoji);
}
}
} else {
// try language-specific "Droid Sans *" and "Noto Sans *" fonts for
// certain blocks, as most devices probably have these
uint8_t block = (aCh >> 8) & 0xff;
switch (block) {
case 0x05:
aFontList.AppendElement("Droid Sans Hebrew");
aFontList.AppendElement("Droid Sans Armenian");
break;
case 0x06:
aFontList.AppendElement("Droid Sans Arabic");
break;
case 0x09:
aFontList.AppendElement("Noto Sans Devanagari");
aFontList.AppendElement("Droid Sans Devanagari");
break;
case 0x0b:
aFontList.AppendElement("Noto Sans Tamil");
aFontList.AppendElement("Droid Sans Tamil");
break;
case 0x0e:
aFontList.AppendElement("Noto Sans Thai");
aFontList.AppendElement("Droid Sans Thai");
break;
case 0x10: case 0x2d:
aFontList.AppendElement("Droid Sans Georgian");
break;
case 0x12: case 0x13:
aFontList.AppendElement("Droid Sans Ethiopic");
break;
case 0xf9: case 0xfa:
if (IsJapaneseLocale()) {
aFontList.AppendElement(kMotoyaLMaru);
aFontList.AppendElement(kNotoSansCJKJP);
aFontList.AppendElement(kDroidSansJapanese);
}
break;
default:
if (block >= 0x2e && block <= 0x9f && IsJapaneseLocale()) {
aFontList.AppendElement(kMotoyaLMaru);
aFontList.AppendElement(kNotoSansCJKJP);
aFontList.AppendElement(kDroidSansJapanese);
}
break;
}
}
// and try Droid Sans Fallback as a last resort
aFontList.AppendElement("Droid Sans Fallback");
}
void
gfxAndroidPlatform::GetSystemFontList(InfallibleTArray<FontListEntry>* retValue)
{
gfxFT2FontList::PlatformFontList()->GetSystemFontList(retValue);
}
gfxPlatformFontList*
gfxAndroidPlatform::CreatePlatformFontList()
{
gfxPlatformFontList* list = new gfxFT2FontList();
if (NS_SUCCEEDED(list->InitFontList())) {
return list;
}
gfxPlatformFontList::Shutdown();
return nullptr;
}
bool
gfxAndroidPlatform::IsFontFormatSupported(nsIURI *aFontURI, uint32_t aFormatFlags)
{
// check for strange format flags
NS_ASSERTION(!(aFormatFlags & gfxUserFontSet::FLAG_FORMAT_NOT_USED),
"strange font format hint set");
// accept supported formats
if (aFormatFlags & gfxUserFontSet::FLAG_FORMATS_COMMON) {
return true;
}
// reject all other formats, known and unknown
if (aFormatFlags != 0) {
return false;
}
// no format hint set, need to look at data
return true;
}
gfxFontGroup *
gfxAndroidPlatform::CreateFontGroup(const FontFamilyList& aFontFamilyList,
const gfxFontStyle* aStyle,
gfxTextPerfMetrics* aTextPerf,
gfxUserFontSet* aUserFontSet,
gfxFloat aDevToCssSize)
{
return new gfxFontGroup(aFontFamilyList, aStyle, aTextPerf,
aUserFontSet, aDevToCssSize);
}
FT_Library
gfxAndroidPlatform::GetFTLibrary()
{
return gPlatformFTLibrary;
}
already_AddRefed<ScaledFont>
gfxAndroidPlatform::GetScaledFontForFont(DrawTarget* aTarget, gfxFont *aFont)
{
return GetScaledFontForFontWithCairoSkia(aTarget, aFont);
}
bool
gfxAndroidPlatform::FontHintingEnabled()
{
// In "mobile" builds, we sometimes use non-reflow-zoom, so we
// might not want hinting. Let's see.
#ifdef MOZ_WIDGET_ANDROID
// On Android, we currently only use gecko to render web
// content that can always be be non-reflow-zoomed. So turn off
// hinting.
//
// XXX when gecko-android-java is used as an "app runtime", we may
// want to re-enable hinting for non-browser processes there.
return false;
#endif // MOZ_WIDGET_ANDROID
// Currently, we don't have any other targets, but if/when we do,
// decide how to handle them here.
NS_NOTREACHED("oops, what platform is this?");
return gfxPlatform::FontHintingEnabled();
}
bool
gfxAndroidPlatform::RequiresLinearZoom()
{
#ifdef MOZ_WIDGET_ANDROID
// On Android, we currently only use gecko to render web
// content that can always be be non-reflow-zoomed.
//
// XXX when gecko-android-java is used as an "app runtime", we may
// want to treat it like B2G and use linear zoom only for the web
// browser process, not other apps.
return true;
#endif
NS_NOTREACHED("oops, what platform is this?");
return gfxPlatform::RequiresLinearZoom();
}
already_AddRefed<mozilla::gfx::VsyncSource>
gfxAndroidPlatform::CreateHardwareVsyncSource()
{
return gfxPlatform::CreateHardwareVsyncSource();
}

View file

@ -0,0 +1,85 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_PLATFORM_ANDROID_H
#define GFX_PLATFORM_ANDROID_H
#include "gfxFT2Fonts.h"
#include "gfxPlatform.h"
#include "gfxUserFontSet.h"
#include "nsCOMPtr.h"
#include "nsTArray.h"
namespace mozilla {
namespace dom {
class FontListEntry;
};
};
using mozilla::dom::FontListEntry;
typedef struct FT_LibraryRec_ *FT_Library;
class gfxAndroidPlatform : public gfxPlatform {
public:
gfxAndroidPlatform();
virtual ~gfxAndroidPlatform();
static gfxAndroidPlatform *GetPlatform() {
return (gfxAndroidPlatform*) gfxPlatform::GetPlatform();
}
virtual already_AddRefed<gfxASurface>
CreateOffscreenSurface(const IntSize& aSize,
gfxImageFormat aFormat) override;
virtual gfxImageFormat GetOffscreenFormat() override { return mOffscreenFormat; }
already_AddRefed<mozilla::gfx::ScaledFont>
GetScaledFontForFont(mozilla::gfx::DrawTarget* aTarget, gfxFont *aFont) override;
// to support IPC font list (sharing between chrome and content)
void GetSystemFontList(InfallibleTArray<FontListEntry>* retValue);
// platform implementations of font functions
virtual bool IsFontFormatSupported(nsIURI *aFontURI, uint32_t aFormatFlags) override;
virtual gfxPlatformFontList* CreatePlatformFontList() override;
virtual void GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh,
Script aRunScript,
nsTArray<const char*>& aFontList) override;
gfxFontGroup*
CreateFontGroup(const mozilla::FontFamilyList& aFontFamilyList,
const gfxFontStyle *aStyle,
gfxTextPerfMetrics* aTextPerf,
gfxUserFontSet *aUserFontSet,
gfxFloat aDevToCssSize) override;
virtual bool FontHintingEnabled() override;
virtual bool RequiresLinearZoom() override;
FT_Library GetFTLibrary();
virtual bool CanRenderContentToDataSurface() const override {
return true;
}
virtual already_AddRefed<mozilla::gfx::VsyncSource> CreateHardwareVsyncSource() override;
virtual bool SupportsApzTouchInput() const override {
return true;
}
protected:
bool AccelerateLayersByDefault() override {
return true;
}
private:
gfxImageFormat mOffscreenFormat;
};
#endif /* GFX_PLATFORM_ANDROID_H */

View file

@ -0,0 +1,11 @@
// vim:set ts=4 sts=4 sw=4 et cin:
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gfxBaseSharedMemorySurface.h"
#include "cairo.h"
const cairo_user_data_key_t SHM_KEY = {0};

View file

@ -0,0 +1,199 @@
// vim:set ts=4 sts=4 sw=4 et cin:
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_SHARED_MEMORYSURFACE_H
#define GFX_SHARED_MEMORYSURFACE_H
#include "mozilla/gfx/2D.h"
#include "mozilla/ipc/Shmem.h"
#include "mozilla/ipc/SharedMemory.h"
#include "gfxASurface.h"
#include "gfxImageSurface.h"
#include "pratom.h"
typedef struct _cairo_user_data_key cairo_user_data_key_t;
struct SharedImageInfo {
int32_t width;
int32_t height;
gfxImageFormat format;
int32_t readCount;
};
inline SharedImageInfo*
GetShmInfoPtr(const mozilla::ipc::Shmem& aShmem)
{
return reinterpret_cast<SharedImageInfo*>
(aShmem.get<char>() + aShmem.Size<char>() - sizeof(SharedImageInfo));
}
extern const cairo_user_data_key_t SHM_KEY;
template <typename Base, typename Sub>
class gfxBaseSharedMemorySurface : public Base {
typedef mozilla::ipc::SharedMemory SharedMemory;
typedef mozilla::ipc::Shmem Shmem;
protected:
virtual ~gfxBaseSharedMemorySurface()
{
MOZ_COUNT_DTOR(gfxBaseSharedMemorySurface);
}
public:
/**
* Return a new gfxSharedImageSurface around a shmem segment newly
* allocated by this function. |aAllocator| is the object used to
* allocate the new shmem segment. Null is returned if creating
* the surface failed.
*
* NB: the *caller* is responsible for freeing the Shmem allocated
* by this function.
*/
template<class ShmemAllocator>
static already_AddRefed<Sub>
Create(ShmemAllocator* aAllocator,
const mozilla::gfx::IntSize& aSize,
gfxImageFormat aFormat,
SharedMemory::SharedMemoryType aShmType = SharedMemory::TYPE_BASIC)
{
return Create<ShmemAllocator, false>(aAllocator, aSize, aFormat, aShmType);
}
/**
* Return a new gfxSharedImageSurface that wraps a shmem segment
* already created by the Create() above. Bad things will happen
* if an attempt is made to wrap any other shmem segment. Null is
* returned if creating the surface failed.
*/
static already_AddRefed<Sub>
Open(const Shmem& aShmem)
{
SharedImageInfo* shmInfo = GetShmInfoPtr(aShmem);
mozilla::gfx::IntSize size(shmInfo->width, shmInfo->height);
if (!mozilla::gfx::Factory::CheckSurfaceSize(size))
return nullptr;
gfxImageFormat format = shmInfo->format;
long stride = gfxImageSurface::ComputeStride(size, format);
RefPtr<Sub> s =
new Sub(size,
stride,
format,
aShmem);
// We didn't create this Shmem and so don't free it on errors
return (s->CairoStatus() != 0) ? nullptr : s.forget();
}
template<class ShmemAllocator>
static already_AddRefed<Sub>
CreateUnsafe(ShmemAllocator* aAllocator,
const mozilla::gfx::IntSize& aSize,
gfxImageFormat aFormat,
SharedMemory::SharedMemoryType aShmType = SharedMemory::TYPE_BASIC)
{
return Create<ShmemAllocator, true>(aAllocator, aSize, aFormat, aShmType);
}
Shmem& GetShmem() { return mShmem; }
static bool IsSharedImage(gfxASurface *aSurface)
{
return (aSurface
&& aSurface->GetType() == gfxSurfaceType::Image
&& aSurface->GetData(&SHM_KEY));
}
protected:
gfxBaseSharedMemorySurface(const mozilla::gfx::IntSize& aSize, long aStride,
gfxImageFormat aFormat,
const Shmem& aShmem)
: Base(aShmem.get<unsigned char>(), aSize, aStride, aFormat)
{
MOZ_COUNT_CTOR(gfxBaseSharedMemorySurface);
mShmem = aShmem;
this->SetData(&SHM_KEY, this, nullptr);
}
private:
void WriteShmemInfo()
{
SharedImageInfo* shmInfo = GetShmInfoPtr(mShmem);
shmInfo->width = this->mSize.width;
shmInfo->height = this->mSize.height;
shmInfo->format = this->mFormat;
shmInfo->readCount = 0;
}
int32_t
ReadLock()
{
SharedImageInfo* shmInfo = GetShmInfoPtr(mShmem);
return PR_ATOMIC_INCREMENT(&shmInfo->readCount);
}
int32_t
ReadUnlock()
{
SharedImageInfo* shmInfo = GetShmInfoPtr(mShmem);
return PR_ATOMIC_DECREMENT(&shmInfo->readCount);
}
int32_t
GetReadCount()
{
SharedImageInfo* shmInfo = GetShmInfoPtr(mShmem);
return shmInfo->readCount;
}
static size_t GetAlignedSize(const mozilla::gfx::IntSize& aSize, long aStride)
{
#define MOZ_ALIGN_WORD(x) (((x) + 3) & ~3)
return MOZ_ALIGN_WORD(sizeof(SharedImageInfo) + aSize.height * aStride);
}
template<class ShmemAllocator, bool Unsafe>
static already_AddRefed<Sub>
Create(ShmemAllocator* aAllocator,
const mozilla::gfx::IntSize& aSize,
gfxImageFormat aFormat,
SharedMemory::SharedMemoryType aShmType)
{
if (!mozilla::gfx::Factory::CheckSurfaceSize(aSize))
return nullptr;
Shmem shmem;
long stride = gfxImageSurface::ComputeStride(aSize, aFormat);
size_t size = GetAlignedSize(aSize, stride);
if (!Unsafe) {
if (!aAllocator->AllocShmem(size, aShmType, &shmem))
return nullptr;
} else {
if (!aAllocator->AllocUnsafeShmem(size, aShmType, &shmem))
return nullptr;
}
RefPtr<Sub> s =
new Sub(aSize, stride, aFormat, shmem);
if (s->CairoStatus() != 0) {
aAllocator->DeallocShmem(shmem);
return nullptr;
}
s->WriteShmemInfo();
return s.forget();
}
Shmem mShmem;
// Calling these is very bad, disallow it
gfxBaseSharedMemorySurface(const gfxBaseSharedMemorySurface&);
gfxBaseSharedMemorySurface& operator=(const gfxBaseSharedMemorySurface&);
};
#endif /* GFX_SHARED_MEMORYSURFACE_H */

1088
gfx/thebes/gfxBlur.cpp Normal file

File diff suppressed because it is too large Load diff

194
gfx/thebes/gfxBlur.h Normal file
View file

@ -0,0 +1,194 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_BLUR_H
#define GFX_BLUR_H
#include "gfxTypes.h"
#include "nsSize.h"
#include "gfxPoint.h"
#include "mozilla/RefPtr.h"
#include "mozilla/UniquePtr.h"
class gfxContext;
struct gfxRect;
namespace mozilla {
namespace gfx {
class AlphaBoxBlur;
struct Color;
struct RectCornerRadii;
class SourceSurface;
class DrawTarget;
} // namespace gfx
} // namespace mozilla
/**
* Implementation of a triple box blur approximation of a Gaussian blur.
*
* A Gaussian blur is good for blurring because, when done independently
* in the horizontal and vertical directions, it matches the result that
* would be obtained using a different (rotated) set of axes. A triple
* box blur is a very close approximation of a Gaussian.
*
* Creates an 8-bit alpha channel context for callers to draw in,
* spreads the contents of that context, blurs the contents, and applies
* it as an alpha mask on a different existing context.
*
* A spread N makes each output pixel the maximum value of all source
* pixels within a square of side length 2N+1 centered on the output pixel.
*
* A temporary surface is created in the Init function. The caller then draws
* any desired content onto the context acquired through GetContext, and lastly
* calls Paint to apply the blurred content as an alpha mask.
*/
class gfxAlphaBoxBlur
{
typedef mozilla::gfx::Color Color;
typedef mozilla::gfx::DrawTarget DrawTarget;
typedef mozilla::gfx::RectCornerRadii RectCornerRadii;
public:
gfxAlphaBoxBlur();
~gfxAlphaBoxBlur();
/**
* Constructs a box blur and initializes the temporary surface.
* @param aRect The coordinates of the surface to create in device units.
*
* @param aBlurRadius The blur radius in pixels. This is the radius of
* the entire (triple) kernel function. Each individual box blur has
* radius approximately 1/3 this value, or diameter approximately 2/3
* this value. This parameter should nearly always be computed using
* CalculateBlurRadius, below.
*
* @param aDirtyRect A pointer to a dirty rect, measured in device units,
* if available. This will be used for optimizing the blur operation. It
* is safe to pass nullptr here.
*
* @param aSkipRect A pointer to a rect, measured in device units, that
* represents an area where blurring is unnecessary and shouldn't be done
* for speed reasons. It is safe to pass nullptr here.
*/
gfxContext* Init(const gfxRect& aRect,
const mozilla::gfx::IntSize& aSpreadRadius,
const mozilla::gfx::IntSize& aBlurRadius,
const gfxRect* aDirtyRect,
const gfxRect* aSkipRect);
/**
* Returns the context that should be drawn to supply the alpha mask to be
* blurred. If the returned surface is null, then there was an error in
* its creation.
*/
gfxContext* GetContext()
{
return mContext;
}
already_AddRefed<mozilla::gfx::SourceSurface>
DoBlur(DrawTarget* aDT, mozilla::gfx::IntPoint* aTopLeft);
/**
* Does the actual blurring/spreading and mask applying. Users of this
* object must have drawn whatever they want to be blurred onto the internal
* gfxContext returned by GetContext before calling this.
*
* @param aDestinationCtx The graphics context on which to apply the
* blurred mask.
*/
void Paint(gfxContext* aDestinationCtx);
/**
* Calculates a blur radius that, when used with box blur, approximates
* a Gaussian blur with the given standard deviation. The result of
* this function should be used as the aBlurRadius parameter to Init,
* above.
*/
static mozilla::gfx::IntSize CalculateBlurRadius(const gfxPoint& aStandardDeviation);
/**
* Blurs a coloured rectangle onto aDestinationCtx. This is equivalent
* to calling Init(), drawing a rectangle onto the returned surface
* and then calling Paint, but may let us optimize better in the
* backend.
*
* @param aDestinationCtx The destination to blur to.
* @param aRect The rectangle to blur in device pixels.
* @param aCornerRadii Corner radii for aRect, if it is a rounded
* rectangle.
* @param aBlurRadius The standard deviation of the blur.
* @param aShadowColor The color to draw the blurred shadow.
* @param aDirtyRect An area in device pixels that is dirty and needs
* to be redrawn.
* @param aSkipRect An area in device pixels to avoid blurring over,
* to prevent unnecessary work.
*/
static void BlurRectangle(gfxContext *aDestinationCtx,
const gfxRect& aRect,
RectCornerRadii* aCornerRadii,
const gfxPoint& aBlurStdDev,
const Color& aShadowColor,
const gfxRect& aDirtyRect,
const gfxRect& aSkipRect);
static void ShutdownBlurCache();
/***
* Blurs an inset box shadow according to a given path.
* This is equivalent to calling Init(), drawing the inset path,
* and calling paint. Do not call Init() if using this method.
*
* @param aDestinationCtx The destination to blur to.
* @param aDestinationRect The destination rect in device pixels
* @param aShadowClipRect The destiniation inner rect of the
* inset path in device pixels.
* @param aBlurRadius The standard deviation of the blur.
* @param aSpreadRadius The spread radius in device pixels.
* @param aShadowColor The color of the blur.
* @param aHasBorderRadius If this element also has a border radius
* @param aInnerClipRadii Corner radii for the inside rect if it is a rounded rect.
* @param aSKipRect An area in device pixels we don't have to paint in.
*/
void BlurInsetBox(gfxContext* aDestinationCtx,
const mozilla::gfx::Rect aDestinationRect,
const mozilla::gfx::Rect aShadowClipRect,
const mozilla::gfx::IntSize aBlurRadius,
const mozilla::gfx::IntSize aSpreadRadius,
const mozilla::gfx::Color& aShadowColor,
const bool aHasBorderRadius,
const RectCornerRadii& aInnerClipRadii,
const mozilla::gfx::Rect aSkipRect,
const mozilla::gfx::Point aShadowOffset);
protected:
already_AddRefed<mozilla::gfx::SourceSurface>
GetInsetBlur(const mozilla::gfx::Rect aOuterRect,
const mozilla::gfx::Rect aWhitespaceRect,
const bool aIsDestRect,
const mozilla::gfx::Color& aShadowColor,
const mozilla::gfx::IntSize& aBlurRadius,
const bool aHasBorderRadius,
const RectCornerRadii& aInnerClipRadii,
DrawTarget* aDestDrawTarget);
/**
* The context of the temporary alpha surface.
*/
RefPtr<gfxContext> mContext;
/**
* The temporary alpha surface.
*/
mozilla::UniquePtr<unsigned char[]> mData;
/**
* The object that actually does the blurring for us.
*/
mozilla::UniquePtr<mozilla::gfx::AlphaBoxBlur> mBlur;
};
#endif /* GFX_BLUR_H */

83
gfx/thebes/gfxColor.h Normal file
View file

@ -0,0 +1,83 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_COLOR_H
#define GFX_COLOR_H
#include "mozilla/Attributes.h" // for MOZ_ALWAYS_INLINE
#include "mozilla/EndianUtils.h" // for mozilla::NativeEndian::swapToBigEndian
/**
* GFX_BLOCK_RGB_TO_FRGB(from,to)
* sizeof(*from) == sizeof(char)
* sizeof(*to) == sizeof(uint32_t)
*
* Copy 4 pixels at a time, reading blocks of 12 bytes (RGB x4)
* and writing blocks of 16 bytes (FRGB x4)
*/
#define GFX_BLOCK_RGB_TO_FRGB(from,to) \
PR_BEGIN_MACRO \
uint32_t m0 = ((uint32_t*)from)[0], \
m1 = ((uint32_t*)from)[1], \
m2 = ((uint32_t*)from)[2], \
rgbr = mozilla::NativeEndian::swapToBigEndian(m0), \
gbrg = mozilla::NativeEndian::swapToBigEndian(m1), \
brgb = mozilla::NativeEndian::swapToBigEndian(m2), \
p0, p1, p2, p3; \
p0 = 0xFF000000 | ((rgbr) >> 8); \
p1 = 0xFF000000 | ((rgbr) << 16) | ((gbrg) >> 16); \
p2 = 0xFF000000 | ((gbrg) << 8) | ((brgb) >> 24); \
p3 = 0xFF000000 | (brgb); \
to[0] = p0; to[1] = p1; to[2] = p2; to[3] = p3; \
PR_END_MACRO
/**
* Fast approximate division by 255. It has the property that
* for all 0 <= n <= 255*255, GFX_DIVIDE_BY_255(n) == n/255.
* But it only uses two adds and two shifts instead of an
* integer division (which is expensive on many processors).
*
* equivalent to ((v)/255)
*/
#define GFX_DIVIDE_BY_255(v) \
(((((unsigned)(v)) << 8) + ((unsigned)(v)) + 255) >> 16)
/**
* Fast premultiply
*
* equivalent to (((c)*(a))/255)
*/
uint8_t MOZ_ALWAYS_INLINE gfxPreMultiply(uint8_t c, uint8_t a) {
return GFX_DIVIDE_BY_255((c)*(a));
}
/**
* Pack the 4 8-bit channels (A,R,G,B)
* into a 32-bit packed NON-premultiplied pixel.
*/
uint32_t MOZ_ALWAYS_INLINE
gfxPackedPixelNoPreMultiply(uint8_t a, uint8_t r, uint8_t g, uint8_t b) {
return (((a) << 24) | ((r) << 16) | ((g) << 8) | (b));
}
/**
* Pack the 4 8-bit channels (A,R,G,B)
* into a 32-bit packed premultiplied pixel.
*/
uint32_t MOZ_ALWAYS_INLINE
gfxPackedPixel(uint8_t a, uint8_t r, uint8_t g, uint8_t b) {
if (a == 0x00)
return 0x00000000;
else if (a == 0xFF) {
return gfxPackedPixelNoPreMultiply(a, r, g, b);
} else {
return ((a) << 24) |
(gfxPreMultiply(r,a) << 16) |
(gfxPreMultiply(g,a) << 8) |
(gfxPreMultiply(b,a));
}
}
#endif /* _GFX_COLOR_H */

1257
gfx/thebes/gfxContext.cpp Normal file

File diff suppressed because it is too large Load diff

694
gfx/thebes/gfxContext.h Normal file
View file

@ -0,0 +1,694 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_CONTEXT_H
#define GFX_CONTEXT_H
#include "gfxTypes.h"
#include "gfxASurface.h"
#include "gfxPoint.h"
#include "gfxRect.h"
#include "gfxMatrix.h"
#include "gfxPattern.h"
#include "nsTArray.h"
#include "mozilla/gfx/2D.h"
typedef struct _cairo cairo_t;
class GlyphBufferAzure;
namespace mozilla {
namespace gfx {
struct RectCornerRadii;
} // namespace gfx
} // namespace mozilla
class ClipExporter;
/**
* This is the main class for doing actual drawing. It is initialized using
* a surface and can be drawn on. It manages various state information like
* a current transformation matrix (CTM), a current path, current color,
* etc.
*
* All drawing happens by creating a path and then stroking or filling it.
* The functions like Rectangle and Arc do not do any drawing themselves.
* When a path is drawn (stroked or filled), it is filled/stroked with a
* pattern set by SetPattern, SetColor or SetSource.
*
* Note that the gfxContext takes coordinates in device pixels,
* as opposed to app units.
*/
class gfxContext final {
typedef mozilla::gfx::CapStyle CapStyle;
typedef mozilla::gfx::CompositionOp CompositionOp;
typedef mozilla::gfx::JoinStyle JoinStyle;
typedef mozilla::gfx::FillRule FillRule;
typedef mozilla::gfx::Path Path;
typedef mozilla::gfx::Pattern Pattern;
typedef mozilla::gfx::Rect Rect;
typedef mozilla::gfx::RectCornerRadii RectCornerRadii;
typedef mozilla::gfx::Size Size;
NS_INLINE_DECL_REFCOUNTING(gfxContext)
public:
/**
* Initialize this context from a DrawTarget.
* Strips any transform from aTarget.
* aTarget will be flushed in the gfxContext's destructor.
* If aTarget is null or invalid, nullptr is returned. The caller
* is responsible for handling this scenario as appropriate.
*/
static already_AddRefed<gfxContext>
CreateOrNull(mozilla::gfx::DrawTarget* aTarget,
const mozilla::gfx::Point& aDeviceOffset = mozilla::gfx::Point());
/**
* Create a new gfxContext wrapping aTarget and preserving aTarget's
* transform. Note that the transform is moved from aTarget to the resulting
* gfxContext, aTarget will no longer have its transform.
* If aTarget is null or invalid, nullptr is returned. The caller
* is responsible for handling this scenario as appropriate.
*/
static already_AddRefed<gfxContext>
CreatePreservingTransformOrNull(mozilla::gfx::DrawTarget* aTarget);
mozilla::gfx::DrawTarget *GetDrawTarget() { return mDT; }
/**
** State
**/
// XXX document exactly what bits are saved
void Save();
void Restore();
/**
** Paths & Drawing
**/
/**
* Fill the current path according to the current settings.
*
* Does not consume the current path.
*/
void Fill();
void Fill(const Pattern& aPattern);
/**
* Forgets the current path.
*/
void NewPath();
/**
* Closes the path, i.e. connects the last drawn point to the first one.
*
* Filling a path will implicitly close it.
*/
void ClosePath();
/**
* Returns the current path.
*/
already_AddRefed<Path> GetPath();
/**
* Sets the given path as the current path.
*/
void SetPath(Path* path);
/**
* Moves the pen to a new point without drawing a line.
*/
void MoveTo(const gfxPoint& pt);
/**
* Returns the current point in the current path.
*/
gfxPoint CurrentPoint();
/**
* Draws a line from the current point to pt.
*
* @see MoveTo
*/
void LineTo(const gfxPoint& pt);
// path helpers
/**
* Draws a line from start to end.
*/
void Line(const gfxPoint& start, const gfxPoint& end); // XXX snapToPixels option?
/**
* Draws the rectangle given by rect.
* @param snapToPixels ?
*/
void Rectangle(const gfxRect& rect, bool snapToPixels = false);
void SnappedRectangle(const gfxRect& rect) { return Rectangle(rect, true); }
/**
** Transformation Matrix manipulation
**/
/**
* Post-multiplies 'other' onto the current CTM, i.e. this
* matrix's transformation will take place before the previously set
* transformations.
*/
void Multiply(const gfxMatrix& other);
/**
* Replaces the current transformation matrix with matrix.
*/
void SetMatrix(const gfxMatrix& matrix);
/**
* Returns the current transformation matrix.
*/
gfxMatrix CurrentMatrix() const;
/**
* Converts a point from device to user coordinates using the inverse
* transformation matrix.
*/
gfxPoint DeviceToUser(const gfxPoint& point) const;
/**
* Converts a size from device to user coordinates. This does not apply
* translation components of the matrix.
*/
Size DeviceToUser(const Size& size) const;
/**
* Converts a rectangle from device to user coordinates; this has the
* same effect as using DeviceToUser on both the rectangle's point and
* size.
*/
gfxRect DeviceToUser(const gfxRect& rect) const;
/**
* Converts a point from user to device coordinates using the transformation
* matrix.
*/
gfxPoint UserToDevice(const gfxPoint& point) const;
/**
* Converts a size from user to device coordinates. This does not apply
* translation components of the matrix.
*/
Size UserToDevice(const Size& size) const;
/**
* Converts a rectangle from user to device coordinates. The
* resulting rectangle is the minimum device-space rectangle that
* encloses the user-space rectangle given.
*/
gfxRect UserToDevice(const gfxRect& rect) const;
/**
* Takes the given rect and tries to align it to device pixels. If
* this succeeds, the method will return true, and the rect will
* be in device coordinates (already transformed by the CTM). If it
* fails, the method will return false, and the rect will not be
* changed.
*
* If ignoreScale is true, then snapping will take place even if
* the CTM has a scale applied. Snapping never takes place if
* there is a rotation in the CTM.
*/
bool UserToDevicePixelSnapped(gfxRect& rect, bool ignoreScale = false) const;
/**
* Takes the given point and tries to align it to device pixels. If
* this succeeds, the method will return true, and the point will
* be in device coordinates (already transformed by the CTM). If it
* fails, the method will return false, and the point will not be
* changed.
*
* If ignoreScale is true, then snapping will take place even if
* the CTM has a scale applied. Snapping never takes place if
* there is a rotation in the CTM.
*/
bool UserToDevicePixelSnapped(gfxPoint& pt, bool ignoreScale = false) const;
/**
** Painting sources
**/
/**
* Set a solid color to use for drawing. This color is in the device color space
* and is not transformed.
*/
void SetDeviceColor(const mozilla::gfx::Color& aColor);
/**
* Gets the current color. It's returned in the device color space.
* returns false if there is something other than a color
* set as the current source (pattern, surface, etc)
*/
bool GetDeviceColor(mozilla::gfx::Color& aColorOut);
/**
* Set a solid color in the sRGB color space to use for drawing.
* If CMS is not enabled, the color is treated as a device-space color
* and this call is identical to SetDeviceColor().
*/
void SetColor(const mozilla::gfx::Color& aColor);
/**
* Uses a surface for drawing. This is a shorthand for creating a
* pattern and setting it.
*
* @param offset from the source surface, to use only part of it.
* May need to make it negative.
*/
void SetSource(gfxASurface *surface, const gfxPoint& offset = gfxPoint(0.0, 0.0));
/**
* Uses a pattern for drawing.
*/
void SetPattern(gfxPattern *pattern);
/**
* Set the color that text drawn on top of transparent pixels should be
* anti-aliased into.
*/
void SetFontSmoothingBackgroundColor(const mozilla::gfx::Color& aColor);
mozilla::gfx::Color GetFontSmoothingBackgroundColor();
/**
* Get the source pattern (solid color, normal pattern, surface, etc)
*/
already_AddRefed<gfxPattern> GetPattern();
/**
** Painting
**/
/**
* Paints the current source surface/pattern everywhere in the current
* clip region.
*/
void Paint(gfxFloat alpha = 1.0);
/**
** Painting with a Mask
**/
/**
* Like Paint, except that it only draws the source where pattern is
* non-transparent.
*/
void Mask(mozilla::gfx::SourceSurface *aSurface, mozilla::gfx::Float aAlpha, const mozilla::gfx::Matrix& aTransform);
void Mask(mozilla::gfx::SourceSurface *aSurface, const mozilla::gfx::Matrix& aTransform) { Mask(aSurface, 1.0f, aTransform); }
void Mask(mozilla::gfx::SourceSurface *surface, float alpha = 1.0f, const mozilla::gfx::Point& offset = mozilla::gfx::Point());
/**
** Line Properties
**/
void SetDash(gfxFloat *dashes, int ndash, gfxFloat offset);
// Return true if dashing is set, false if it's not enabled or the
// context is in an error state. |offset| can be nullptr to mean
// "don't care".
bool CurrentDash(FallibleTArray<gfxFloat>& dashes, gfxFloat* offset) const;
// Returns 0.0 if dashing isn't enabled.
gfxFloat CurrentDashOffset() const;
/**
* Sets the line width that's used for line drawing.
*/
void SetLineWidth(gfxFloat width);
/**
* Returns the currently set line width.
*
* @see SetLineWidth
*/
gfxFloat CurrentLineWidth() const;
/**
* Sets the line caps, i.e. how line endings are drawn.
*/
void SetLineCap(CapStyle cap);
CapStyle CurrentLineCap() const;
/**
* Sets the line join, i.e. how the connection between two lines is
* drawn.
*/
void SetLineJoin(JoinStyle join);
JoinStyle CurrentLineJoin() const;
void SetMiterLimit(gfxFloat limit);
gfxFloat CurrentMiterLimit() const;
/**
* Sets the operator used for all further drawing. The operator affects
* how drawing something will modify the destination. For example, the
* OVER operator will do alpha blending of source and destination, while
* SOURCE will replace the destination with the source.
*/
void SetOp(CompositionOp op);
CompositionOp CurrentOp() const;
void SetAntialiasMode(mozilla::gfx::AntialiasMode mode);
mozilla::gfx::AntialiasMode CurrentAntialiasMode() const;
/**
** Clipping
**/
/**
* Clips all further drawing to the current path.
* This does not consume the current path.
*/
void Clip();
/**
* Helper functions that will create a rect path and call Clip().
* Any current path will be destroyed by these functions!
*/
void Clip(const Rect& rect);
void Clip(const gfxRect& rect); // will clip to a rect
void Clip(Path* aPath);
void PopClip();
/**
* This will return the current bounds of the clip region in user
* space.
*/
gfxRect GetClipExtents();
/**
* Whether the current clip is not a simple rectangle.
*/
bool HasComplexClip() const;
/**
* Returns true if the given rectangle is fully contained in the current clip.
* This is conservative; it may return false even when the given rectangle is
* fully contained by the current clip.
*/
bool ClipContainsRect(const gfxRect& aRect);
/**
* Exports the current clip using the provided exporter.
*/
bool ExportClip(ClipExporter& aExporter);
/**
* Groups
*/
void PushGroupForBlendBack(gfxContentType content, mozilla::gfx::Float aOpacity = 1.0f,
mozilla::gfx::SourceSurface* aMask = nullptr,
const mozilla::gfx::Matrix& aMaskTransform = mozilla::gfx::Matrix());
/**
* Like PushGroupForBlendBack, but if the current surface is gfxContentType::COLOR and
* content is gfxContentType::COLOR_ALPHA, makes the pushed surface gfxContentType::COLOR
* instead and copies the contents of the current surface to the pushed
* surface. This is good for pushing opacity groups, since blending the
* group back to the current surface with some alpha applied will give
* the correct results and using an opaque pushed surface gives better
* quality and performance.
*/
void PushGroupAndCopyBackground(gfxContentType content = gfxContentType::COLOR,
mozilla::gfx::Float aOpacity = 1.0f,
mozilla::gfx::SourceSurface* aMask = nullptr,
const mozilla::gfx::Matrix& aMaskTransform = mozilla::gfx::Matrix());
void PopGroupAndBlend();
mozilla::gfx::Point GetDeviceOffset() const;
#ifdef MOZ_DUMP_PAINTING
/**
* Debug functions to encode the current surface as a PNG and export it.
*/
/**
* Writes a binary PNG file.
*/
void WriteAsPNG(const char* aFile);
/**
* Write as a PNG encoded Data URL to stdout.
*/
void DumpAsDataURI();
/**
* Copy a PNG encoded Data URL to the clipboard.
*/
void CopyAsDataURI();
#endif
static mozilla::gfx::UserDataKey sDontUseAsSourceKey;
private:
/**
* Initialize this context from a DrawTarget.
* Strips any transform from aTarget.
* aTarget will be flushed in the gfxContext's destructor. Use the static
* ContextForDrawTargetNoTransform() when you want this behavior, as that
* version deals with null DrawTarget better.
*/
explicit gfxContext(mozilla::gfx::DrawTarget *aTarget,
const mozilla::gfx::Point& aDeviceOffset = mozilla::gfx::Point());
~gfxContext();
friend class PatternFromState;
friend class GlyphBufferAzure;
typedef mozilla::gfx::Matrix Matrix;
typedef mozilla::gfx::DrawTarget DrawTarget;
typedef mozilla::gfx::Color Color;
typedef mozilla::gfx::StrokeOptions StrokeOptions;
typedef mozilla::gfx::Float Float;
typedef mozilla::gfx::PathBuilder PathBuilder;
typedef mozilla::gfx::SourceSurface SourceSurface;
struct AzureState {
AzureState()
: op(mozilla::gfx::CompositionOp::OP_OVER)
, color(0, 0, 0, 1.0f)
, aaMode(mozilla::gfx::AntialiasMode::SUBPIXEL)
, patternTransformChanged(false)
, mBlendOpacity(0.0f)
{}
mozilla::gfx::CompositionOp op;
Color color;
RefPtr<gfxPattern> pattern;
RefPtr<gfxASurface> sourceSurfCairo;
RefPtr<SourceSurface> sourceSurface;
mozilla::gfx::Point sourceSurfaceDeviceOffset;
Matrix surfTransform;
Matrix transform;
struct PushedClip {
RefPtr<Path> path;
Rect rect;
Matrix transform;
};
nsTArray<PushedClip> pushedClips;
nsTArray<Float> dashPattern;
StrokeOptions strokeOptions;
RefPtr<DrawTarget> drawTarget;
mozilla::gfx::AntialiasMode aaMode;
bool patternTransformChanged;
Matrix patternTransform;
Color fontSmoothingBackgroundColor;
// This is used solely for using minimal intermediate surface size.
mozilla::gfx::Point deviceOffset;
// Support groups
mozilla::gfx::Float mBlendOpacity;
RefPtr<SourceSurface> mBlendMask;
Matrix mBlendMaskTransform;
#ifdef DEBUG
bool mWasPushedForBlendBack;
#endif
};
// This ensures mPath contains a valid path (in user space!)
void EnsurePath();
// This ensures mPathBuilder contains a valid PathBuilder (in user space!)
void EnsurePathBuilder();
void FillAzure(const Pattern& aPattern, mozilla::gfx::Float aOpacity);
void PushClipsToDT(mozilla::gfx::DrawTarget *aDT);
CompositionOp GetOp();
void ChangeTransform(const mozilla::gfx::Matrix &aNewMatrix, bool aUpdatePatternTransform = true);
Rect GetAzureDeviceSpaceClipBounds();
Matrix GetDeviceTransform() const;
Matrix GetDTTransform() const;
void PushNewDT(gfxContentType content);
bool mPathIsRect;
bool mTransformChanged;
Matrix mPathTransform;
Rect mRect;
RefPtr<PathBuilder> mPathBuilder;
RefPtr<Path> mPath;
Matrix mTransform;
nsTArray<AzureState> mStateStack;
AzureState &CurrentState() { return mStateStack[mStateStack.Length() - 1]; }
const AzureState &CurrentState() const { return mStateStack[mStateStack.Length() - 1]; }
RefPtr<DrawTarget> mDT;
};
/**
* Sentry helper class for functions with multiple return points that need to
* call Save() on a gfxContext and have Restore() called automatically on the
* gfxContext before they return.
*/
class gfxContextAutoSaveRestore
{
public:
gfxContextAutoSaveRestore() : mContext(nullptr) {}
explicit gfxContextAutoSaveRestore(gfxContext *aContext) : mContext(aContext) {
mContext->Save();
}
~gfxContextAutoSaveRestore() {
Restore();
}
void SetContext(gfxContext *aContext) {
NS_ASSERTION(!mContext, "Not going to call Restore() on some context!!!");
mContext = aContext;
mContext->Save();
}
void EnsureSaved(gfxContext *aContext) {
MOZ_ASSERT(!mContext || mContext == aContext, "wrong context");
if (!mContext) {
mContext = aContext;
mContext->Save();
}
}
void Restore() {
if (mContext) {
mContext->Restore();
mContext = nullptr;
}
}
private:
gfxContext *mContext;
};
/**
* Sentry helper class for functions with multiple return points that need to
* back up the current matrix of a context and have it automatically restored
* before they return.
*/
class gfxContextMatrixAutoSaveRestore
{
public:
gfxContextMatrixAutoSaveRestore() :
mContext(nullptr)
{
}
explicit gfxContextMatrixAutoSaveRestore(gfxContext *aContext) :
mContext(aContext), mMatrix(aContext->CurrentMatrix())
{
}
~gfxContextMatrixAutoSaveRestore()
{
if (mContext) {
mContext->SetMatrix(mMatrix);
}
}
void SetContext(gfxContext *aContext)
{
NS_ASSERTION(!mContext,
"Not going to restore the matrix on some context!");
mContext = aContext;
mMatrix = aContext->CurrentMatrix();
}
void Restore()
{
if (mContext) {
mContext->SetMatrix(mMatrix);
mContext = nullptr;
}
}
const gfxMatrix& Matrix()
{
MOZ_ASSERT(mContext, "mMatrix doesn't contain a useful matrix");
return mMatrix;
}
bool HasMatrix() const { return !!mContext; }
private:
gfxContext *mContext;
gfxMatrix mMatrix;
};
class DrawTargetAutoDisableSubpixelAntialiasing {
public:
typedef mozilla::gfx::DrawTarget DrawTarget;
DrawTargetAutoDisableSubpixelAntialiasing(DrawTarget *aDT, bool aDisable)
{
if (aDisable) {
mDT = aDT;
mSubpixelAntialiasingEnabled = mDT->GetPermitSubpixelAA();
mDT->SetPermitSubpixelAA(false);
}
}
~DrawTargetAutoDisableSubpixelAntialiasing()
{
if (mDT) {
mDT->SetPermitSubpixelAA(mSubpixelAntialiasingEnabled);
}
}
private:
RefPtr<DrawTarget> mDT;
bool mSubpixelAntialiasingEnabled;
};
/* This class lives on the stack and allows gfxContext users to easily, and
* performantly get a gfx::Pattern to use for drawing in their current context.
*/
class PatternFromState
{
public:
explicit PatternFromState(gfxContext *aContext) : mContext(aContext), mPattern(nullptr) {}
~PatternFromState() { if (mPattern) { mPattern->~Pattern(); } }
operator mozilla::gfx::Pattern&();
private:
union {
mozilla::AlignedStorage2<mozilla::gfx::ColorPattern> mColorPattern;
mozilla::AlignedStorage2<mozilla::gfx::SurfacePattern> mSurfacePattern;
};
gfxContext *mContext;
mozilla::gfx::Pattern *mPattern;
};
/* This interface should be implemented to handle exporting the clip from a context.
*/
class ClipExporter : public mozilla::gfx::PathSink {
public:
virtual void BeginClip(const mozilla::gfx::Matrix& aMatrix) = 0;
virtual void EndClip() = 0;
};
#endif /* GFX_CONTEXT_H */

View file

@ -0,0 +1,800 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/ArrayUtils.h"
#include "gfxCoreTextShaper.h"
#include "gfxMacFont.h"
#include "gfxFontUtils.h"
#include "gfxTextRun.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/UniquePtrExtensions.h"
#include <algorithm>
#include <dlfcn.h>
using namespace mozilla;
// standard font descriptors that we construct the first time they're needed
CTFontDescriptorRef gfxCoreTextShaper::sDefaultFeaturesDescriptor = nullptr;
CTFontDescriptorRef gfxCoreTextShaper::sDisableLigaturesDescriptor = nullptr;
CTFontDescriptorRef gfxCoreTextShaper::sIndicFeaturesDescriptor = nullptr;
CTFontDescriptorRef gfxCoreTextShaper::sIndicDisableLigaturesDescriptor = nullptr;
static CFStringRef sCTWritingDirectionAttributeName = nullptr;
// See CTStringAttributes.h
enum {
kMyCTWritingDirectionEmbedding = (0 << 1),
kMyCTWritingDirectionOverride = (1 << 1)
};
// Helper to create a CFDictionary with the right attributes for shaping our
// text, including imposing the given directionality.
// This will only be called if we're on 10.8 or later.
CFDictionaryRef
gfxCoreTextShaper::CreateAttrDict(bool aRightToLeft)
{
// Because we always shape unidirectional runs, and may have applied
// directional overrides, we want to force a direction rather than
// allowing CoreText to do its own unicode-based bidi processing.
SInt16 dirOverride = kMyCTWritingDirectionOverride |
(aRightToLeft ? kCTWritingDirectionRightToLeft
: kCTWritingDirectionLeftToRight);
CFNumberRef dirNumber =
::CFNumberCreate(kCFAllocatorDefault,
kCFNumberSInt16Type, &dirOverride);
CFArrayRef dirArray =
::CFArrayCreate(kCFAllocatorDefault,
(const void **) &dirNumber, 1,
&kCFTypeArrayCallBacks);
::CFRelease(dirNumber);
CFTypeRef attrs[] = { kCTFontAttributeName, sCTWritingDirectionAttributeName };
CFTypeRef values[] = { mCTFont, dirArray };
CFDictionaryRef attrDict =
::CFDictionaryCreate(kCFAllocatorDefault,
attrs, values, ArrayLength(attrs),
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
::CFRelease(dirArray);
return attrDict;
}
CFDictionaryRef
gfxCoreTextShaper::CreateAttrDictWithoutDirection()
{
CFTypeRef attrs[] = { kCTFontAttributeName };
CFTypeRef values[] = { mCTFont };
CFDictionaryRef attrDict =
::CFDictionaryCreate(kCFAllocatorDefault,
attrs, values, ArrayLength(attrs),
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
return attrDict;
}
gfxCoreTextShaper::gfxCoreTextShaper(gfxMacFont *aFont)
: gfxFontShaper(aFont)
, mAttributesDictLTR(nullptr)
, mAttributesDictRTL(nullptr)
{
static bool sInitialized = false;
if (!sInitialized) {
CFStringRef* pstr = (CFStringRef*)
dlsym(RTLD_DEFAULT, "kCTWritingDirectionAttributeName");
if (pstr) {
sCTWritingDirectionAttributeName = *pstr;
}
sInitialized = true;
}
// Create our CTFontRef
mCTFont = CreateCTFontWithFeatures(aFont->GetAdjustedSize(),
GetDefaultFeaturesDescriptor());
}
gfxCoreTextShaper::~gfxCoreTextShaper()
{
if (mAttributesDictLTR) {
::CFRelease(mAttributesDictLTR);
}
if (mAttributesDictRTL) {
::CFRelease(mAttributesDictRTL);
}
if (mCTFont) {
::CFRelease(mCTFont);
}
}
static bool
IsBuggyIndicScript(unicode::Script aScript)
{
return aScript == unicode::Script::BENGALI ||
aScript == unicode::Script::KANNADA;
}
bool
gfxCoreTextShaper::ShapeText(DrawTarget *aDrawTarget,
const char16_t *aText,
uint32_t aOffset,
uint32_t aLength,
Script aScript,
bool aVertical,
gfxShapedText *aShapedText)
{
// Create a CFAttributedString with text and style info, so we can use CoreText to lay it out.
bool isRightToLeft = aShapedText->IsRightToLeft();
const UniChar* text = reinterpret_cast<const UniChar*>(aText);
uint32_t length = aLength;
uint32_t startOffset;
CFStringRef stringObj;
CFDictionaryRef attrObj;
if (sCTWritingDirectionAttributeName) {
startOffset = 0;
stringObj = ::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault,
text, length,
kCFAllocatorNull);
// Get an attributes dictionary suitable for shaping text in the
// current direction, creating it if necessary.
attrObj = isRightToLeft ? mAttributesDictRTL : mAttributesDictLTR;
if (!attrObj) {
attrObj = CreateAttrDict(isRightToLeft);
(isRightToLeft ? mAttributesDictRTL : mAttributesDictLTR) = attrObj;
}
} else {
// OS is too old to support kCTWritingDirectionAttributeName:
// we need to bidi-wrap the text if the run is RTL,
// or if it is an LTR run but may contain (overridden) RTL chars
bool bidiWrap = isRightToLeft;
if (!bidiWrap && !aShapedText->TextIs8Bit()) {
uint32_t i;
for (i = 0; i < length; ++i) {
if (gfxFontUtils::PotentialRTLChar(aText[i])) {
bidiWrap = true;
break;
}
}
}
// If there's a possibility of any bidi, we wrap the text with
// direction overrides to ensure neutrals or characters that were
// bidi-overridden in HTML behave properly.
static const UniChar beginLTR[] = { 0x202d, 0x20 };
static const UniChar beginRTL[] = { 0x202e, 0x20 };
static const UniChar endBidiWrap[] = { 0x20, 0x2e, 0x202c };
if (bidiWrap) {
startOffset = isRightToLeft ? ArrayLength(beginRTL)
: ArrayLength(beginLTR);
CFMutableStringRef mutableString =
::CFStringCreateMutable(kCFAllocatorDefault,
length + startOffset +
ArrayLength(endBidiWrap));
::CFStringAppendCharacters(mutableString,
isRightToLeft ? beginRTL : beginLTR,
startOffset);
::CFStringAppendCharacters(mutableString, text, length);
::CFStringAppendCharacters(mutableString, endBidiWrap,
ArrayLength(endBidiWrap));
stringObj = mutableString;
} else {
startOffset = 0;
stringObj =
::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault,
text, length,
kCFAllocatorNull);
}
// Get an attributes dictionary suitable for shaping text,
// creating it if necessary. (This dict is not LTR-specific,
// but we use that field to store it anyway.)
if (!mAttributesDictLTR) {
mAttributesDictLTR = CreateAttrDictWithoutDirection();
}
attrObj = mAttributesDictLTR;
}
CTFontRef tempCTFont = nullptr;
if (IsBuggyIndicScript(aScript)) {
// To work around buggy Indic AAT fonts shipped with OS X,
// we re-enable the Line Initial Smart Swashes feature that is needed
// for "split vowels" to work in at least Bengali and Kannada fonts.
// Affected fonts include Bangla MN, Bangla Sangam MN, Kannada MN,
// Kannada Sangam MN. See bugs 686225, 728557, 953231, 1145515.
tempCTFont =
CreateCTFontWithFeatures(::CTFontGetSize(mCTFont),
aShapedText->DisableLigatures()
? GetIndicDisableLigaturesDescriptor()
: GetIndicFeaturesDescriptor());
} else if (aShapedText->DisableLigatures()) {
// For letterspacing (or maybe other situations) we need to make
// a copy of the CTFont with the ligature feature disabled.
tempCTFont =
CreateCTFontWithFeatures(::CTFontGetSize(mCTFont),
GetDisableLigaturesDescriptor());
}
// For the disabled-ligature or buggy-indic-font case, we need to replace
// the standard CTFont in the attribute dictionary with a tweaked version.
CFMutableDictionaryRef mutableAttr = nullptr;
if (tempCTFont) {
mutableAttr = ::CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 2,
attrObj);
::CFDictionaryReplaceValue(mutableAttr,
kCTFontAttributeName, tempCTFont);
// Having created the dict, we're finished with our temporary
// Indic and/or ligature-disabled CTFontRef.
::CFRelease(tempCTFont);
attrObj = mutableAttr;
}
// Now we can create an attributed string
CFAttributedStringRef attrStringObj =
::CFAttributedStringCreate(kCFAllocatorDefault, stringObj, attrObj);
::CFRelease(stringObj);
// Create the CoreText line from our string, then we're done with it
CTLineRef line = ::CTLineCreateWithAttributedString(attrStringObj);
::CFRelease(attrStringObj);
// and finally retrieve the glyph data and store into the gfxTextRun
CFArrayRef glyphRuns = ::CTLineGetGlyphRuns(line);
uint32_t numRuns = ::CFArrayGetCount(glyphRuns);
// Iterate through the glyph runs.
// Note that this includes the bidi wrapper, so we have to be careful
// not to include the extra glyphs from there
bool success = true;
for (uint32_t runIndex = 0; runIndex < numRuns; runIndex++) {
CTRunRef aCTRun =
(CTRunRef)::CFArrayGetValueAtIndex(glyphRuns, runIndex);
// If the range is purely within bidi-wrapping text, ignore it.
CFRange range = ::CTRunGetStringRange(aCTRun);
if (uint32_t(range.location + range.length) <= startOffset ||
range.location - startOffset >= aLength) {
continue;
}
CFDictionaryRef runAttr = ::CTRunGetAttributes(aCTRun);
if (runAttr != attrObj) {
// If Core Text manufactured a new dictionary, this may indicate
// unexpected font substitution. In that case, we fail (and fall
// back to harfbuzz shaping)...
const void* font1 =
::CFDictionaryGetValue(attrObj, kCTFontAttributeName);
const void* font2 =
::CFDictionaryGetValue(runAttr, kCTFontAttributeName);
if (font1 != font2) {
// ...except that if the fallback was only for a variation
// selector or join control that is otherwise unsupported,
// we just ignore it.
if (range.length == 1) {
char16_t ch = aText[range.location - startOffset];
if (gfxFontUtils::IsJoinControl(ch) ||
gfxFontUtils::IsVarSelector(ch)) {
continue;
}
}
NS_WARNING("unexpected font fallback in Core Text");
success = false;
break;
}
}
if (SetGlyphsFromRun(aShapedText, aOffset, aLength, aCTRun,
startOffset) != NS_OK) {
success = false;
break;
}
}
if (mutableAttr) {
::CFRelease(mutableAttr);
}
::CFRelease(line);
return success;
}
#define SMALL_GLYPH_RUN 128 // preallocated size of our auto arrays for per-glyph data;
// some testing indicates that 90%+ of glyph runs will fit
// without requiring a separate allocation
nsresult
gfxCoreTextShaper::SetGlyphsFromRun(gfxShapedText *aShapedText,
uint32_t aOffset,
uint32_t aLength,
CTRunRef aCTRun,
int32_t aStringOffset)
{
// The word has been bidi-wrapped; aStringOffset is the number
// of chars at the beginning of the CTLine that we should skip.
// aCTRun is a glyph run from the CoreText layout process.
int32_t direction = aShapedText->IsRightToLeft() ? -1 : 1;
int32_t numGlyphs = ::CTRunGetGlyphCount(aCTRun);
if (numGlyphs == 0) {
return NS_OK;
}
int32_t wordLength = aLength;
// character offsets get really confusing here, as we have to keep track of
// (a) the text in the actual textRun we're constructing
// (c) the string that was handed to CoreText, which contains the text of the font run
// plus directional-override padding
// (d) the CTRun currently being processed, which may be a sub-run of the CoreText line
// (but may extend beyond the actual font run into the bidi wrapping text).
// aStringOffset tells us how many initial characters of the line to ignore.
// get the source string range within the CTLine's text
CFRange stringRange = ::CTRunGetStringRange(aCTRun);
// skip the run if it is entirely outside the actual range of the font run
if (stringRange.location - aStringOffset + stringRange.length <= 0 ||
stringRange.location - aStringOffset >= wordLength) {
return NS_OK;
}
// retrieve the laid-out glyph data from the CTRun
UniquePtr<CGGlyph[]> glyphsArray;
UniquePtr<CGPoint[]> positionsArray;
UniquePtr<CFIndex[]> glyphToCharArray;
const CGGlyph* glyphs = nullptr;
const CGPoint* positions = nullptr;
const CFIndex* glyphToChar = nullptr;
// Testing indicates that CTRunGetGlyphsPtr (almost?) always succeeds,
// and so allocating a new array and copying data with CTRunGetGlyphs
// will be extremely rare.
// If this were not the case, we could use an AutoTArray<> to
// try and avoid the heap allocation for small runs.
// It's possible that some future change to CoreText will mean that
// CTRunGetGlyphsPtr fails more often; if this happens, AutoTArray<>
// may become an attractive option.
glyphs = ::CTRunGetGlyphsPtr(aCTRun);
if (!glyphs) {
glyphsArray = MakeUniqueFallible<CGGlyph[]>(numGlyphs);
if (!glyphsArray) {
return NS_ERROR_OUT_OF_MEMORY;
}
::CTRunGetGlyphs(aCTRun, ::CFRangeMake(0, 0), glyphsArray.get());
glyphs = glyphsArray.get();
}
positions = ::CTRunGetPositionsPtr(aCTRun);
if (!positions) {
positionsArray = MakeUniqueFallible<CGPoint[]>(numGlyphs);
if (!positionsArray) {
return NS_ERROR_OUT_OF_MEMORY;
}
::CTRunGetPositions(aCTRun, ::CFRangeMake(0, 0), positionsArray.get());
positions = positionsArray.get();
}
// Remember that the glyphToChar indices relate to the CoreText line,
// not to the beginning of the textRun, the font run,
// or the stringRange of the glyph run
glyphToChar = ::CTRunGetStringIndicesPtr(aCTRun);
if (!glyphToChar) {
glyphToCharArray = MakeUniqueFallible<CFIndex[]>(numGlyphs);
if (!glyphToCharArray) {
return NS_ERROR_OUT_OF_MEMORY;
}
::CTRunGetStringIndices(aCTRun, ::CFRangeMake(0, 0), glyphToCharArray.get());
glyphToChar = glyphToCharArray.get();
}
double runWidth = ::CTRunGetTypographicBounds(aCTRun, ::CFRangeMake(0, 0),
nullptr, nullptr, nullptr);
AutoTArray<gfxShapedText::DetailedGlyph,1> detailedGlyphs;
gfxShapedText::CompressedGlyph *charGlyphs =
aShapedText->GetCharacterGlyphs() + aOffset;
// CoreText gives us the glyphindex-to-charindex mapping, which relates each glyph
// to a source text character; we also need the charindex-to-glyphindex mapping to
// find the glyph for a given char. Note that some chars may not map to any glyph
// (ligature continuations), and some may map to several glyphs (eg Indic split vowels).
// We set the glyph index to NO_GLYPH for chars that have no associated glyph, and we
// record the last glyph index for cases where the char maps to several glyphs,
// so that our clumping will include all the glyph fragments for the character.
// The charToGlyph array is indexed by char position within the stringRange of the glyph run.
static const int32_t NO_GLYPH = -1;
AutoTArray<int32_t,SMALL_GLYPH_RUN> charToGlyphArray;
if (!charToGlyphArray.SetLength(stringRange.length, fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
int32_t *charToGlyph = charToGlyphArray.Elements();
for (int32_t offset = 0; offset < stringRange.length; ++offset) {
charToGlyph[offset] = NO_GLYPH;
}
for (int32_t i = 0; i < numGlyphs; ++i) {
int32_t loc = glyphToChar[i] - stringRange.location;
if (loc >= 0 && loc < stringRange.length) {
charToGlyph[loc] = i;
}
}
// Find character and glyph clumps that correspond, allowing for ligatures,
// indic reordering, split glyphs, etc.
//
// The idea is that we'll find a character sequence starting at the first char of stringRange,
// and extend it until it includes the character associated with the first glyph;
// we also extend it as long as there are "holes" in the range of glyphs. So we
// will eventually have a contiguous sequence of characters, starting at the beginning
// of the range, that map to a contiguous sequence of glyphs, starting at the beginning
// of the glyph array. That's a clump; then we update the starting positions and repeat.
//
// NB: In the case of RTL layouts, we iterate over the stringRange in reverse.
//
// This may find characters that fall outside the range 0:wordLength,
// so we won't necessarily use everything we find here.
bool isRightToLeft = aShapedText->IsRightToLeft();
int32_t glyphStart = 0; // looking for a clump that starts at this glyph index
int32_t charStart = isRightToLeft ?
stringRange.length - 1 : 0; // and this char index (in the stringRange of the glyph run)
while (glyphStart < numGlyphs) { // keep finding groups until all glyphs are accounted for
bool inOrder = true;
int32_t charEnd = glyphToChar[glyphStart] - stringRange.location;
NS_WARNING_ASSERTION(
charEnd >= 0 && charEnd < stringRange.length,
"glyph-to-char mapping points outside string range");
// clamp charEnd to the valid range of the string
charEnd = std::max(charEnd, 0);
charEnd = std::min(charEnd, int32_t(stringRange.length));
int32_t glyphEnd = glyphStart;
int32_t charLimit = isRightToLeft ? -1 : stringRange.length;
do {
// This is normally executed once for each iteration of the outer loop,
// but in unusual cases where the character/glyph association is complex,
// the initial character range might correspond to a non-contiguous
// glyph range with "holes" in it. If so, we will repeat this loop to
// extend the character range until we have a contiguous glyph sequence.
NS_ASSERTION((direction > 0 && charEnd < charLimit) ||
(direction < 0 && charEnd > charLimit),
"no characters left in range?");
charEnd += direction;
while (charEnd != charLimit && charToGlyph[charEnd] == NO_GLYPH) {
charEnd += direction;
}
// find the maximum glyph index covered by the clump so far
if (isRightToLeft) {
for (int32_t i = charStart; i > charEnd; --i) {
if (charToGlyph[i] != NO_GLYPH) {
// update extent of glyph range
glyphEnd = std::max(glyphEnd, charToGlyph[i] + 1);
}
}
} else {
for (int32_t i = charStart; i < charEnd; ++i) {
if (charToGlyph[i] != NO_GLYPH) {
// update extent of glyph range
glyphEnd = std::max(glyphEnd, charToGlyph[i] + 1);
}
}
}
if (glyphEnd == glyphStart + 1) {
// for the common case of a single-glyph clump, we can skip the following checks
break;
}
if (glyphEnd == glyphStart) {
// no glyphs, try to extend the clump
continue;
}
// check whether all glyphs in the range are associated with the characters
// in our clump; if not, we have a discontinuous range, and should extend it
// unless we've reached the end of the text
bool allGlyphsAreWithinCluster = true;
int32_t prevGlyphCharIndex = charStart;
for (int32_t i = glyphStart; i < glyphEnd; ++i) {
int32_t glyphCharIndex = glyphToChar[i] - stringRange.location;
if (isRightToLeft) {
if (glyphCharIndex > charStart || glyphCharIndex <= charEnd) {
allGlyphsAreWithinCluster = false;
break;
}
if (glyphCharIndex > prevGlyphCharIndex) {
inOrder = false;
}
prevGlyphCharIndex = glyphCharIndex;
} else {
if (glyphCharIndex < charStart || glyphCharIndex >= charEnd) {
allGlyphsAreWithinCluster = false;
break;
}
if (glyphCharIndex < prevGlyphCharIndex) {
inOrder = false;
}
prevGlyphCharIndex = glyphCharIndex;
}
}
if (allGlyphsAreWithinCluster) {
break;
}
} while (charEnd != charLimit);
NS_WARNING_ASSERTION(glyphStart < glyphEnd,
"character/glyph clump contains no glyphs!");
if (glyphStart == glyphEnd) {
++glyphStart; // make progress - avoid potential infinite loop
charStart = charEnd;
continue;
}
NS_WARNING_ASSERTION(charStart != charEnd,
"character/glyph clump contains no characters!");
if (charStart == charEnd) {
glyphStart = glyphEnd; // this is bad - we'll discard the glyph(s),
// as there's nowhere to attach them
continue;
}
// Now charStart..charEnd is a ligature clump, corresponding to glyphStart..glyphEnd;
// Set baseCharIndex to the char we'll actually attach the glyphs to (1st of ligature),
// and endCharIndex to the limit (position beyond the last char),
// adjusting for the offset of the stringRange relative to the textRun.
int32_t baseCharIndex, endCharIndex;
if (isRightToLeft) {
while (charEnd >= 0 && charToGlyph[charEnd] == NO_GLYPH) {
charEnd--;
}
baseCharIndex = charEnd + stringRange.location - aStringOffset + 1;
endCharIndex = charStart + stringRange.location - aStringOffset + 1;
} else {
while (charEnd < stringRange.length && charToGlyph[charEnd] == NO_GLYPH) {
charEnd++;
}
baseCharIndex = charStart + stringRange.location - aStringOffset;
endCharIndex = charEnd + stringRange.location - aStringOffset;
}
// Then we check if the clump falls outside our actual string range; if so, just go to the next.
if (endCharIndex <= 0 || baseCharIndex >= wordLength) {
glyphStart = glyphEnd;
charStart = charEnd;
continue;
}
// Ensure we won't try to go beyond the valid length of the word's text
baseCharIndex = std::max(baseCharIndex, 0);
endCharIndex = std::min(endCharIndex, wordLength);
// Now we're ready to set the glyph info in the textRun; measure the glyph width
// of the first (perhaps only) glyph, to see if it is "Simple"
int32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit();
double toNextGlyph;
if (glyphStart < numGlyphs-1) {
toNextGlyph = positions[glyphStart+1].x - positions[glyphStart].x;
} else {
toNextGlyph = positions[0].x + runWidth - positions[glyphStart].x;
}
int32_t advance = int32_t(toNextGlyph * appUnitsPerDevUnit);
// Check if it's a simple one-to-one mapping
int32_t glyphsInClump = glyphEnd - glyphStart;
if (glyphsInClump == 1 &&
gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyphs[glyphStart]) &&
gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
charGlyphs[baseCharIndex].IsClusterStart() &&
positions[glyphStart].y == 0.0)
{
charGlyphs[baseCharIndex].SetSimpleGlyph(advance,
glyphs[glyphStart]);
} else {
// collect all glyphs in a list to be assigned to the first char;
// there must be at least one in the clump, and we already measured its advance,
// hence the placement of the loop-exit test and the measurement of the next glyph
while (1) {
gfxTextRun::DetailedGlyph *details = detailedGlyphs.AppendElement();
details->mGlyphID = glyphs[glyphStart];
details->mXOffset = 0;
details->mYOffset = -positions[glyphStart].y * appUnitsPerDevUnit;
details->mAdvance = advance;
if (++glyphStart >= glyphEnd) {
break;
}
if (glyphStart < numGlyphs-1) {
toNextGlyph = positions[glyphStart+1].x - positions[glyphStart].x;
} else {
toNextGlyph = positions[0].x + runWidth - positions[glyphStart].x;
}
advance = int32_t(toNextGlyph * appUnitsPerDevUnit);
}
gfxTextRun::CompressedGlyph textRunGlyph;
textRunGlyph.SetComplex(charGlyphs[baseCharIndex].IsClusterStart(),
true, detailedGlyphs.Length());
aShapedText->SetGlyphs(aOffset + baseCharIndex, textRunGlyph,
detailedGlyphs.Elements());
detailedGlyphs.Clear();
}
// the rest of the chars in the group are ligature continuations, no associated glyphs
while (++baseCharIndex != endCharIndex && baseCharIndex < wordLength) {
gfxShapedText::CompressedGlyph &shapedTextGlyph = charGlyphs[baseCharIndex];
NS_ASSERTION(!shapedTextGlyph.IsSimpleGlyph(), "overwriting a simple glyph");
shapedTextGlyph.SetComplex(inOrder && shapedTextGlyph.IsClusterStart(), false, 0);
}
glyphStart = glyphEnd;
charStart = charEnd;
}
return NS_OK;
}
#undef SMALL_GLYPH_RUN
// Construct the font attribute descriptor that we'll apply by default when
// creating a CTFontRef. This will turn off line-edge swashes by default,
// because we don't know the actual line breaks when doing glyph shaping.
// We also cache feature descriptors for shaping with disabled ligatures, and
// for buggy Indic AAT font workarounds, created on an as-needed basis.
#define MAX_FEATURES 3 // max used by any of our Get*Descriptor functions
CTFontDescriptorRef
gfxCoreTextShaper::CreateFontFeaturesDescriptor(
const std::pair<SInt16,SInt16> aFeatures[],
size_t aCount)
{
MOZ_ASSERT(aCount <= MAX_FEATURES);
CFDictionaryRef featureSettings[MAX_FEATURES];
for (size_t i = 0; i < aCount; i++) {
CFNumberRef type = ::CFNumberCreate(kCFAllocatorDefault,
kCFNumberSInt16Type,
&aFeatures[i].first);
CFNumberRef selector = ::CFNumberCreate(kCFAllocatorDefault,
kCFNumberSInt16Type,
&aFeatures[i].second);
CFTypeRef keys[] = { kCTFontFeatureTypeIdentifierKey,
kCTFontFeatureSelectorIdentifierKey };
CFTypeRef values[] = { type, selector };
featureSettings[i] =
::CFDictionaryCreate(kCFAllocatorDefault,
(const void **) keys,
(const void **) values,
ArrayLength(keys),
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
::CFRelease(selector);
::CFRelease(type);
}
CFArrayRef featuresArray =
::CFArrayCreate(kCFAllocatorDefault,
(const void **) featureSettings,
aCount, // not ArrayLength(featureSettings), as we
// may not have used all the allocated slots
&kCFTypeArrayCallBacks);
for (size_t i = 0; i < aCount; i++) {
::CFRelease(featureSettings[i]);
}
const CFTypeRef attrKeys[] = { kCTFontFeatureSettingsAttribute };
const CFTypeRef attrValues[] = { featuresArray };
CFDictionaryRef attributesDict =
::CFDictionaryCreate(kCFAllocatorDefault,
(const void **) attrKeys,
(const void **) attrValues,
ArrayLength(attrKeys),
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
::CFRelease(featuresArray);
CTFontDescriptorRef descriptor =
::CTFontDescriptorCreateWithAttributes(attributesDict);
::CFRelease(attributesDict);
return descriptor;
}
CTFontDescriptorRef
gfxCoreTextShaper::GetDefaultFeaturesDescriptor()
{
if (sDefaultFeaturesDescriptor == nullptr) {
const std::pair<SInt16,SInt16> kDefaultFeatures[] = {
{ kSmartSwashType, kLineInitialSwashesOffSelector },
{ kSmartSwashType, kLineFinalSwashesOffSelector }
};
sDefaultFeaturesDescriptor =
CreateFontFeaturesDescriptor(kDefaultFeatures,
ArrayLength(kDefaultFeatures));
}
return sDefaultFeaturesDescriptor;
}
CTFontDescriptorRef
gfxCoreTextShaper::GetDisableLigaturesDescriptor()
{
if (sDisableLigaturesDescriptor == nullptr) {
const std::pair<SInt16,SInt16> kDisableLigatures[] = {
{ kSmartSwashType, kLineInitialSwashesOffSelector },
{ kSmartSwashType, kLineFinalSwashesOffSelector },
{ kLigaturesType, kCommonLigaturesOffSelector }
};
sDisableLigaturesDescriptor =
CreateFontFeaturesDescriptor(kDisableLigatures,
ArrayLength(kDisableLigatures));
}
return sDisableLigaturesDescriptor;
}
CTFontDescriptorRef
gfxCoreTextShaper::GetIndicFeaturesDescriptor()
{
if (sIndicFeaturesDescriptor == nullptr) {
const std::pair<SInt16,SInt16> kIndicFeatures[] = {
{ kSmartSwashType, kLineFinalSwashesOffSelector }
};
sIndicFeaturesDescriptor =
CreateFontFeaturesDescriptor(kIndicFeatures,
ArrayLength(kIndicFeatures));
}
return sIndicFeaturesDescriptor;
}
CTFontDescriptorRef
gfxCoreTextShaper::GetIndicDisableLigaturesDescriptor()
{
if (sIndicDisableLigaturesDescriptor == nullptr) {
const std::pair<SInt16,SInt16> kIndicDisableLigatures[] = {
{ kSmartSwashType, kLineFinalSwashesOffSelector },
{ kLigaturesType, kCommonLigaturesOffSelector }
};
sIndicDisableLigaturesDescriptor =
CreateFontFeaturesDescriptor(kIndicDisableLigatures,
ArrayLength(kIndicDisableLigatures));
}
return sIndicDisableLigaturesDescriptor;
}
CTFontRef
gfxCoreTextShaper::CreateCTFontWithFeatures(CGFloat aSize,
CTFontDescriptorRef aDescriptor)
{
gfxMacFont *f = static_cast<gfxMacFont*>(mFont);
return ::CTFontCreateWithGraphicsFont(f->GetCGFontRef(), aSize, nullptr,
aDescriptor);
}
void
gfxCoreTextShaper::Shutdown() // [static]
{
if (sIndicDisableLigaturesDescriptor != nullptr) {
::CFRelease(sIndicDisableLigaturesDescriptor);
sIndicDisableLigaturesDescriptor = nullptr;
}
if (sIndicFeaturesDescriptor != nullptr) {
::CFRelease(sIndicFeaturesDescriptor);
sIndicFeaturesDescriptor = nullptr;
}
if (sDisableLigaturesDescriptor != nullptr) {
::CFRelease(sDisableLigaturesDescriptor);
sDisableLigaturesDescriptor = nullptr;
}
if (sDefaultFeaturesDescriptor != nullptr) {
::CFRelease(sDefaultFeaturesDescriptor);
sDefaultFeaturesDescriptor = nullptr;
}
}

View file

@ -0,0 +1,71 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_CORETEXTSHAPER_H
#define GFX_CORETEXTSHAPER_H
#include "gfxFont.h"
#include <ApplicationServices/ApplicationServices.h>
class gfxMacFont;
class gfxCoreTextShaper : public gfxFontShaper {
public:
explicit gfxCoreTextShaper(gfxMacFont *aFont);
virtual ~gfxCoreTextShaper();
virtual bool ShapeText(DrawTarget *aDrawTarget,
const char16_t *aText,
uint32_t aOffset,
uint32_t aLength,
Script aScript,
bool aVertical,
gfxShapedText *aShapedText);
// clean up static objects that may have been cached
static void Shutdown();
protected:
CTFontRef mCTFont;
// attributes for shaping text with LTR or RTL directionality
CFDictionaryRef mAttributesDictLTR;
CFDictionaryRef mAttributesDictRTL;
nsresult SetGlyphsFromRun(gfxShapedText *aShapedText,
uint32_t aOffset,
uint32_t aLength,
CTRunRef aCTRun,
int32_t aStringOffset);
CTFontRef CreateCTFontWithFeatures(CGFloat aSize,
CTFontDescriptorRef aDescriptor);
CFDictionaryRef CreateAttrDict(bool aRightToLeft);
CFDictionaryRef CreateAttrDictWithoutDirection();
static CTFontDescriptorRef
CreateFontFeaturesDescriptor(const std::pair<SInt16,SInt16> aFeatures[],
size_t aCount);
static CTFontDescriptorRef GetDefaultFeaturesDescriptor();
static CTFontDescriptorRef GetDisableLigaturesDescriptor();
static CTFontDescriptorRef GetIndicFeaturesDescriptor();
static CTFontDescriptorRef GetIndicDisableLigaturesDescriptor();
// cached font descriptor, created the first time it's needed
static CTFontDescriptorRef sDefaultFeaturesDescriptor;
// cached descriptor for adding disable-ligatures setting to a font
static CTFontDescriptorRef sDisableLigaturesDescriptor;
// feature descriptors for buggy Indic AAT font workaround
static CTFontDescriptorRef sIndicFeaturesDescriptor;
static CTFontDescriptorRef sIndicDisableLigaturesDescriptor;
};
#endif /* GFX_CORETEXTSHAPER_H */

View file

@ -0,0 +1,182 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gfxDWriteCommon.h"
#include <unordered_map>
#include "mozilla/Atomics.h"
#include "mozilla/gfx/Logging.h"
static mozilla::Atomic<uint64_t> sNextFontFileKey;
static std::unordered_map<uint64_t, IDWriteFontFileStream*> sFontFileStreams;
IDWriteFontFileLoader* gfxDWriteFontFileLoader::mInstance = nullptr;
class gfxDWriteFontFileStream final : public IDWriteFontFileStream
{
public:
/**
* Used by the FontFileLoader to create a new font stream,
* this font stream is created from data in memory. The memory
* passed may be released after object creation, it will be
* copied internally.
*
* @param aData Font data
*/
gfxDWriteFontFileStream(FallibleTArray<uint8_t> *aData,
uint64_t aFontFileKey);
~gfxDWriteFontFileStream();
// IUnknown interface
IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject)
{
if (iid == __uuidof(IDWriteFontFileStream)) {
*ppObject = static_cast<IDWriteFontFileStream*>(this);
return S_OK;
}
else if (iid == __uuidof(IUnknown)) {
*ppObject = static_cast<IUnknown*>(this);
return S_OK;
}
else {
return E_NOINTERFACE;
}
}
IFACEMETHOD_(ULONG, AddRef)()
{
NS_PRECONDITION(int32_t(mRefCnt) >= 0, "illegal refcnt");
++mRefCnt;
return mRefCnt;
}
IFACEMETHOD_(ULONG, Release)()
{
NS_PRECONDITION(0 != mRefCnt, "dup release");
--mRefCnt;
if (mRefCnt == 0) {
delete this;
return 0;
}
return mRefCnt;
}
// IDWriteFontFileStream methods
virtual HRESULT STDMETHODCALLTYPE ReadFileFragment(void const** fragmentStart,
UINT64 fileOffset,
UINT64 fragmentSize,
OUT void** fragmentContext);
virtual void STDMETHODCALLTYPE ReleaseFileFragment(void* fragmentContext);
virtual HRESULT STDMETHODCALLTYPE GetFileSize(OUT UINT64* fileSize);
virtual HRESULT STDMETHODCALLTYPE GetLastWriteTime(OUT UINT64* lastWriteTime);
private:
FallibleTArray<uint8_t> mData;
nsAutoRefCnt mRefCnt;
uint64_t mFontFileKey;
};
gfxDWriteFontFileStream::gfxDWriteFontFileStream(FallibleTArray<uint8_t> *aData,
uint64_t aFontFileKey)
: mFontFileKey(aFontFileKey)
{
mData.SwapElements(*aData);
}
gfxDWriteFontFileStream::~gfxDWriteFontFileStream()
{
sFontFileStreams.erase(mFontFileKey);
}
HRESULT STDMETHODCALLTYPE
gfxDWriteFontFileStream::GetFileSize(UINT64 *fileSize)
{
*fileSize = mData.Length();
return S_OK;
}
HRESULT STDMETHODCALLTYPE
gfxDWriteFontFileStream::GetLastWriteTime(UINT64 *lastWriteTime)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE
gfxDWriteFontFileStream::ReadFileFragment(const void **fragmentStart,
UINT64 fileOffset,
UINT64 fragmentSize,
void **fragmentContext)
{
// We are required to do bounds checking.
if (fileOffset + fragmentSize > (UINT64)mData.Length()) {
return E_FAIL;
}
// We should be alive for the duration of this.
*fragmentStart = &mData[fileOffset];
*fragmentContext = nullptr;
return S_OK;
}
void STDMETHODCALLTYPE
gfxDWriteFontFileStream::ReleaseFileFragment(void *fragmentContext)
{
}
HRESULT STDMETHODCALLTYPE
gfxDWriteFontFileLoader::CreateStreamFromKey(const void *fontFileReferenceKey,
UINT32 fontFileReferenceKeySize,
IDWriteFontFileStream **fontFileStream)
{
if (!fontFileReferenceKey || !fontFileStream) {
return E_POINTER;
}
uint64_t fontFileKey = *static_cast<const uint64_t*>(fontFileReferenceKey);
auto found = sFontFileStreams.find(fontFileKey);
if (found == sFontFileStreams.end()) {
*fontFileStream = nullptr;
return E_FAIL;
}
found->second->AddRef();
*fontFileStream = found->second;
return S_OK;
}
/* static */
HRESULT
gfxDWriteFontFileLoader::CreateCustomFontFile(FallibleTArray<uint8_t>& aFontData,
IDWriteFontFile** aFontFile,
IDWriteFontFileStream** aFontFileStream)
{
MOZ_ASSERT(aFontFile);
MOZ_ASSERT(aFontFileStream);
IDWriteFactory *factory = gfxWindowsPlatform::GetPlatform()->GetDWriteFactory();
if (!factory) {
gfxCriticalError() << "Failed to get DWrite Factory in CreateCustomFontFile.";
return E_FAIL;
}
uint64_t fontFileKey = sNextFontFileKey++;
RefPtr<IDWriteFontFileStream> ffsRef = new gfxDWriteFontFileStream(&aFontData, fontFileKey);
sFontFileStreams[fontFileKey] = ffsRef;
RefPtr<IDWriteFontFile> fontFile;
HRESULT hr = factory->CreateCustomFontFileReference(&fontFileKey, sizeof(fontFileKey), Instance(), getter_AddRefs(fontFile));
if (FAILED(hr)) {
NS_WARNING("Failed to load font file from data!");
return hr;
}
fontFile.forget(aFontFile);
ffsRef.forget(aFontFileStream);
return S_OK;
}

View file

@ -0,0 +1,153 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_DWRITECOMMON_H
#define GFX_DWRITECOMMON_H
// Mozilla includes
#include "nscore.h"
#include "nsIServiceManager.h"
#include "nsCOMPtr.h"
#include "cairo-features.h"
#include "gfxFontConstants.h"
#include "nsTArray.h"
#include "gfxWindowsPlatform.h"
#include "nsIUUIDGenerator.h"
#include <windows.h>
#include <dwrite.h>
static inline DWRITE_FONT_STRETCH
DWriteFontStretchFromStretch(int16_t aStretch)
{
switch (aStretch) {
case NS_FONT_STRETCH_ULTRA_CONDENSED:
return DWRITE_FONT_STRETCH_ULTRA_CONDENSED;
case NS_FONT_STRETCH_EXTRA_CONDENSED:
return DWRITE_FONT_STRETCH_EXTRA_CONDENSED;
case NS_FONT_STRETCH_CONDENSED:
return DWRITE_FONT_STRETCH_CONDENSED;
case NS_FONT_STRETCH_SEMI_CONDENSED:
return DWRITE_FONT_STRETCH_SEMI_CONDENSED;
case NS_FONT_STRETCH_NORMAL:
return DWRITE_FONT_STRETCH_NORMAL;
case NS_FONT_STRETCH_SEMI_EXPANDED:
return DWRITE_FONT_STRETCH_SEMI_EXPANDED;
case NS_FONT_STRETCH_EXPANDED:
return DWRITE_FONT_STRETCH_EXPANDED;
case NS_FONT_STRETCH_EXTRA_EXPANDED:
return DWRITE_FONT_STRETCH_EXTRA_EXPANDED;
case NS_FONT_STRETCH_ULTRA_EXPANDED:
return DWRITE_FONT_STRETCH_ULTRA_EXPANDED;
default:
return DWRITE_FONT_STRETCH_UNDEFINED;
}
}
static inline int16_t
FontStretchFromDWriteStretch(DWRITE_FONT_STRETCH aStretch)
{
switch (aStretch) {
case DWRITE_FONT_STRETCH_ULTRA_CONDENSED:
return NS_FONT_STRETCH_ULTRA_CONDENSED;
case DWRITE_FONT_STRETCH_EXTRA_CONDENSED:
return NS_FONT_STRETCH_EXTRA_CONDENSED;
case DWRITE_FONT_STRETCH_CONDENSED:
return NS_FONT_STRETCH_CONDENSED;
case DWRITE_FONT_STRETCH_SEMI_CONDENSED:
return NS_FONT_STRETCH_SEMI_CONDENSED;
case DWRITE_FONT_STRETCH_NORMAL:
return NS_FONT_STRETCH_NORMAL;
case DWRITE_FONT_STRETCH_SEMI_EXPANDED:
return NS_FONT_STRETCH_SEMI_EXPANDED;
case DWRITE_FONT_STRETCH_EXPANDED:
return NS_FONT_STRETCH_EXPANDED;
case DWRITE_FONT_STRETCH_EXTRA_EXPANDED:
return NS_FONT_STRETCH_EXTRA_EXPANDED;
case DWRITE_FONT_STRETCH_ULTRA_EXPANDED:
return NS_FONT_STRETCH_ULTRA_EXPANDED;
default:
return NS_FONT_STRETCH_NORMAL;
}
}
struct ffReferenceKey
{
FallibleTArray<uint8_t> *mArray;
nsID mGUID;
};
class gfxDWriteFontFileLoader : public IDWriteFontFileLoader
{
public:
gfxDWriteFontFileLoader()
{
}
// IUnknown interface
IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject)
{
if (iid == __uuidof(IDWriteFontFileLoader)) {
*ppObject = static_cast<IDWriteFontFileLoader*>(this);
return S_OK;
} else if (iid == __uuidof(IUnknown)) {
*ppObject = static_cast<IUnknown*>(this);
return S_OK;
} else {
return E_NOINTERFACE;
}
}
IFACEMETHOD_(ULONG, AddRef)()
{
return 1;
}
IFACEMETHOD_(ULONG, Release)()
{
return 1;
}
// IDWriteFontFileLoader methods
/**
* Important! Note the key here -has- to be a pointer to a uint64_t.
*/
virtual HRESULT STDMETHODCALLTYPE
CreateStreamFromKey(void const* fontFileReferenceKey,
UINT32 fontFileReferenceKeySize,
OUT IDWriteFontFileStream** fontFileStream);
/**
* Gets the singleton loader instance. Note that when using this font
* loader, the key must be a pointer to a unint64_t.
*/
static IDWriteFontFileLoader* Instance()
{
if (!mInstance) {
mInstance = new gfxDWriteFontFileLoader();
gfxWindowsPlatform::GetPlatform()->GetDWriteFactory()->
RegisterFontFileLoader(mInstance);
}
return mInstance;
}
/**
* Creates a IDWriteFontFile and IDWriteFontFileStream from aFontData.
* aFontData will be empty on return as it swaps out the data.
*
* @param aFontData the font data for the custom font file
* @param aFontFile out param for the created font file
* @param aFontFileStream out param for the corresponding stream
* @return HRESULT of internal calls
*/
static HRESULT CreateCustomFontFile(FallibleTArray<uint8_t>& aFontData,
IDWriteFontFile** aFontFile,
IDWriteFontFileStream** aFontFileStream);
private:
static IDWriteFontFileLoader* mInstance;
};
#endif /* GFX_DWRITECOMMON_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,445 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_DWRITEFONTLIST_H
#define GFX_DWRITEFONTLIST_H
#include "mozilla/MemoryReporting.h"
#include "gfxDWriteCommon.h"
#include "gfxFont.h"
#include "gfxUserFontSet.h"
#include "cairo-win32.h"
#include "gfxPlatformFontList.h"
#include "gfxPlatform.h"
#include <algorithm>
/**
* gfxDWriteFontFamily is a class that describes one of the fonts on the
* users system. It holds each gfxDWriteFontEntry (maps more directly to
* a font face) which holds font type, charset info and character map info.
*/
class gfxDWriteFontEntry;
/**
* \brief Class representing directwrite font family.
*/
class gfxDWriteFontFamily : public gfxFontFamily
{
public:
/**
* Constructs a new DWriteFont Family.
*
* \param aName Name identifying the family
* \param aFamily IDWriteFontFamily object representing the directwrite
* family object.
*/
gfxDWriteFontFamily(const nsAString& aName,
IDWriteFontFamily *aFamily)
: gfxFontFamily(aName), mDWFamily(aFamily), mForceGDIClassic(false) {}
virtual ~gfxDWriteFontFamily();
void FindStyleVariations(FontInfoData *aFontInfoData = nullptr) final;
void LocalizedName(nsAString& aLocalizedName) final;
void ReadFaceNames(gfxPlatformFontList *aPlatformFontList,
bool aNeedFullnamePostscriptNames,
FontInfoData *aFontInfoData = nullptr) final;
void SetForceGDIClassic(bool aForce) { mForceGDIClassic = aForce; }
void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontListSizes* aSizes) const final;
void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontListSizes* aSizes) const final;
protected:
/** This font family's directwrite fontfamily object */
RefPtr<IDWriteFontFamily> mDWFamily;
bool mForceGDIClassic;
};
/**
* \brief Class representing DirectWrite FontEntry (a unique font style/family)
*/
class gfxDWriteFontEntry : public gfxFontEntry
{
public:
/**
* Constructs a font entry.
*
* \param aFaceName The name of the corresponding font face.
* \param aFont DirectWrite font object
*/
gfxDWriteFontEntry(const nsAString& aFaceName,
IDWriteFont *aFont)
: gfxFontEntry(aFaceName), mFont(aFont), mFontFile(nullptr),
mForceGDIClassic(false)
{
DWRITE_FONT_STYLE dwriteStyle = aFont->GetStyle();
mStyle = (dwriteStyle == DWRITE_FONT_STYLE_ITALIC ?
NS_FONT_STYLE_ITALIC :
(dwriteStyle == DWRITE_FONT_STYLE_OBLIQUE ?
NS_FONT_STYLE_OBLIQUE : NS_FONT_STYLE_NORMAL));
mStretch = FontStretchFromDWriteStretch(aFont->GetStretch());
uint16_t weight = NS_ROUNDUP(aFont->GetWeight() - 50, 100);
weight = std::max<uint16_t>(100, weight);
weight = std::min<uint16_t>(900, weight);
mWeight = weight;
mIsCJK = UNINITIALIZED_VALUE;
}
/**
* Constructs a font entry using a font. But with custom font values.
* This is used for creating correct font entries for @font-face with local
* font source.
*
* \param aFaceName The name of the corresponding font face.
* \param aFont DirectWrite font object
* \param aWeight Weight of the font
* \param aStretch Stretch of the font
* \param aStyle italic or oblique of font
*/
gfxDWriteFontEntry(const nsAString& aFaceName,
IDWriteFont *aFont,
uint16_t aWeight,
int16_t aStretch,
uint8_t aStyle)
: gfxFontEntry(aFaceName), mFont(aFont), mFontFile(nullptr),
mForceGDIClassic(false)
{
mWeight = aWeight;
mStretch = aStretch;
mStyle = aStyle;
mIsLocalUserFont = true;
mIsCJK = UNINITIALIZED_VALUE;
}
/**
* Constructs a font entry using a font file.
*
* \param aFaceName The name of the corresponding font face.
* \param aFontFile DirectWrite fontfile object
* \param aFontFileStream DirectWrite fontfile stream object
* \param aWeight Weight of the font
* \param aStretch Stretch of the font
* \param aStyle italic or oblique of font
*/
gfxDWriteFontEntry(const nsAString& aFaceName,
IDWriteFontFile *aFontFile,
IDWriteFontFileStream *aFontFileStream,
uint16_t aWeight,
int16_t aStretch,
uint8_t aStyle)
: gfxFontEntry(aFaceName), mFont(nullptr), mFontFile(aFontFile),
mFontFileStream(aFontFileStream), mForceGDIClassic(false)
{
mWeight = aWeight;
mStretch = aStretch;
mStyle = aStyle;
mIsDataUserFont = true;
mIsCJK = UNINITIALIZED_VALUE;
}
virtual ~gfxDWriteFontEntry();
virtual bool IsSymbolFont();
virtual hb_blob_t* GetFontTable(uint32_t aTableTag) override;
nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr);
bool IsCJKFont();
void SetForceGDIClassic(bool aForce) { mForceGDIClassic = aForce; }
bool GetForceGDIClassic() { return mForceGDIClassic; }
virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontListSizes* aSizes) const;
virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontListSizes* aSizes) const;
protected:
friend class gfxDWriteFont;
friend class gfxDWriteFontList;
virtual nsresult CopyFontTable(uint32_t aTableTag,
nsTArray<uint8_t>& aBuffer) override;
virtual gfxFont *CreateFontInstance(const gfxFontStyle *aFontStyle,
bool aNeedsBold);
nsresult CreateFontFace(
IDWriteFontFace **aFontFace,
DWRITE_FONT_SIMULATIONS aSimulations = DWRITE_FONT_SIMULATIONS_NONE);
static bool InitLogFont(IDWriteFont *aFont, LOGFONTW *aLogFont);
/**
* A fontentry only needs to have either of these. If it has both only
* the IDWriteFont will be used.
*/
RefPtr<IDWriteFont> mFont;
RefPtr<IDWriteFontFile> mFontFile;
// For custom fonts, we hold a reference to the IDWriteFontFileStream for
// for the IDWriteFontFile, so that the data is available.
RefPtr<IDWriteFontFileStream> mFontFileStream;
// font face corresponding to the mFont/mFontFile *without* any DWrite
// style simulations applied
RefPtr<IDWriteFontFace> mFontFace;
DWRITE_FONT_FACE_TYPE mFaceType;
int8_t mIsCJK;
bool mForceGDIClassic;
};
// custom text renderer used to determine the fallback font for a given char
class DWriteFontFallbackRenderer final : public IDWriteTextRenderer
{
public:
DWriteFontFallbackRenderer(IDWriteFactory *aFactory)
: mRefCount(0)
{
HRESULT hr = S_OK;
hr = aFactory->GetSystemFontCollection(getter_AddRefs(mSystemFonts));
NS_ASSERTION(SUCCEEDED(hr), "GetSystemFontCollection failed!");
}
~DWriteFontFallbackRenderer()
{}
// IDWriteTextRenderer methods
IFACEMETHOD(DrawGlyphRun)(
void* clientDrawingContext,
FLOAT baselineOriginX,
FLOAT baselineOriginY,
DWRITE_MEASURING_MODE measuringMode,
DWRITE_GLYPH_RUN const* glyphRun,
DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,
IUnknown* clientDrawingEffect
);
IFACEMETHOD(DrawUnderline)(
void* clientDrawingContext,
FLOAT baselineOriginX,
FLOAT baselineOriginY,
DWRITE_UNDERLINE const* underline,
IUnknown* clientDrawingEffect
)
{
return E_NOTIMPL;
}
IFACEMETHOD(DrawStrikethrough)(
void* clientDrawingContext,
FLOAT baselineOriginX,
FLOAT baselineOriginY,
DWRITE_STRIKETHROUGH const* strikethrough,
IUnknown* clientDrawingEffect
)
{
return E_NOTIMPL;
}
IFACEMETHOD(DrawInlineObject)(
void* clientDrawingContext,
FLOAT originX,
FLOAT originY,
IDWriteInlineObject* inlineObject,
BOOL isSideways,
BOOL isRightToLeft,
IUnknown* clientDrawingEffect
)
{
return E_NOTIMPL;
}
// IDWritePixelSnapping methods
IFACEMETHOD(IsPixelSnappingDisabled)(
void* clientDrawingContext,
BOOL* isDisabled
)
{
*isDisabled = FALSE;
return S_OK;
}
IFACEMETHOD(GetCurrentTransform)(
void* clientDrawingContext,
DWRITE_MATRIX* transform
)
{
const DWRITE_MATRIX ident = {1.0, 0.0, 0.0, 1.0, 0.0, 0.0};
*transform = ident;
return S_OK;
}
IFACEMETHOD(GetPixelsPerDip)(
void* clientDrawingContext,
FLOAT* pixelsPerDip
)
{
*pixelsPerDip = 1.0f;
return S_OK;
}
// IUnknown methods
IFACEMETHOD_(unsigned long, AddRef) ()
{
return InterlockedIncrement(&mRefCount);
}
IFACEMETHOD_(unsigned long, Release) ()
{
unsigned long newCount = InterlockedDecrement(&mRefCount);
if (newCount == 0)
{
delete this;
return 0;
}
return newCount;
}
IFACEMETHOD(QueryInterface) (IID const& riid, void** ppvObject)
{
if (__uuidof(IDWriteTextRenderer) == riid) {
*ppvObject = this;
} else if (__uuidof(IDWritePixelSnapping) == riid) {
*ppvObject = this;
} else if (__uuidof(IUnknown) == riid) {
*ppvObject = this;
} else {
*ppvObject = nullptr;
return E_FAIL;
}
this->AddRef();
return S_OK;
}
const nsString& FallbackFamilyName() { return mFamilyName; }
protected:
long mRefCount;
RefPtr<IDWriteFontCollection> mSystemFonts;
nsString mFamilyName;
};
class gfxDWriteFontList : public gfxPlatformFontList {
public:
gfxDWriteFontList();
static gfxDWriteFontList* PlatformFontList() {
return static_cast<gfxDWriteFontList*>(sPlatformFontList);
}
// initialize font lists
virtual nsresult InitFontListForPlatform() override;
virtual gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
uint16_t aWeight,
int16_t aStretch,
uint8_t aStyle);
virtual gfxFontEntry* MakePlatformFont(const nsAString& aFontName,
uint16_t aWeight,
int16_t aStretch,
uint8_t aStyle,
const uint8_t* aFontData,
uint32_t aLength);
bool GetStandardFamilyName(const nsAString& aFontName,
nsAString& aFamilyName);
IDWriteGdiInterop *GetGDIInterop() { return mGDIInterop; }
bool UseGDIFontTableAccess() { return mGDIFontTableAccess; }
bool FindAndAddFamilies(const nsAString& aFamily,
nsTArray<gfxFontFamily*>* aOutput,
gfxFontStyle* aStyle = nullptr,
gfxFloat aDevToCssSize = 1.0) override;
gfxFloat GetForceGDIClassicMaxFontSize() { return mForceGDIClassicMaxFontSize; }
virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontListSizes* aSizes) const;
virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontListSizes* aSizes) const;
protected:
virtual gfxFontFamily*
GetDefaultFontForPlatform(const gfxFontStyle* aStyle) override;
// attempt to use platform-specific fallback for the given character,
// return null if no usable result found
gfxFontEntry*
PlatformGlobalFontFallback(const uint32_t aCh,
Script aRunScript,
const gfxFontStyle* aMatchStyle,
gfxFontFamily** aMatchedFamily) override;
private:
friend class gfxDWriteFontFamily;
nsresult GetFontSubstitutes();
void GetDirectWriteSubstitutes();
virtual bool UsesSystemFallback() { return true; }
void GetFontsFromCollection(IDWriteFontCollection* aCollection);
#ifdef MOZ_BUNDLED_FONTS
already_AddRefed<IDWriteFontCollection>
CreateBundledFontsCollection(IDWriteFactory* aFactory);
#endif
/**
* Fonts listed in the registry as substitutes but for which no actual
* font family is found.
*/
nsTArray<nsString> mNonExistingFonts;
/**
* Table of font substitutes, we grab this from the registry to get
* alternative font names.
*/
FontFamilyTable mFontSubstitutes;
virtual already_AddRefed<FontInfoData> CreateFontInfoData();
gfxFloat mForceGDIClassicMaxFontSize;
// whether to use GDI font table access routines
bool mGDIFontTableAccess;
RefPtr<IDWriteGdiInterop> mGDIInterop;
RefPtr<DWriteFontFallbackRenderer> mFallbackRenderer;
RefPtr<IDWriteTextFormat> mFallbackFormat;
RefPtr<IDWriteFontCollection> mSystemFonts;
#ifdef MOZ_BUNDLED_FONTS
RefPtr<IDWriteFontCollection> mBundledFonts;
#endif
};
#endif /* GFX_DWRITEFONTLIST_H */

View file

@ -0,0 +1,709 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gfxDWriteFonts.h"
#include "mozilla/MemoryReporting.h"
#include <algorithm>
#include "gfxDWriteFontList.h"
#include "gfxContext.h"
#include "gfxTextRun.h"
#include <dwrite.h>
#include "harfbuzz/hb.h"
using namespace mozilla;
using namespace mozilla::gfx;
// This is also in gfxGDIFont.cpp. Would be nice to put it somewhere common,
// but we can't declare it in the gfxFont.h or gfxFontUtils.h headers
// because those are exported, and the cairo headers aren't.
static inline cairo_antialias_t
GetCairoAntialiasOption(gfxFont::AntialiasOption anAntialiasOption)
{
switch (anAntialiasOption) {
default:
case gfxFont::kAntialiasDefault:
return CAIRO_ANTIALIAS_DEFAULT;
case gfxFont::kAntialiasNone:
return CAIRO_ANTIALIAS_NONE;
case gfxFont::kAntialiasGrayscale:
return CAIRO_ANTIALIAS_GRAY;
case gfxFont::kAntialiasSubpixel:
return CAIRO_ANTIALIAS_SUBPIXEL;
}
}
// Code to determine whether Windows is set to use ClearType font smoothing;
// based on private functions in cairo-win32-font.c
#ifndef SPI_GETFONTSMOOTHINGTYPE
#define SPI_GETFONTSMOOTHINGTYPE 0x200a
#endif
#ifndef FE_FONTSMOOTHINGCLEARTYPE
#define FE_FONTSMOOTHINGCLEARTYPE 2
#endif
static bool
UsingClearType()
{
BOOL fontSmoothing;
if (!SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &fontSmoothing, 0) ||
!fontSmoothing)
{
return false;
}
UINT type;
if (SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &type, 0) &&
type == FE_FONTSMOOTHINGCLEARTYPE)
{
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
// gfxDWriteFont
gfxDWriteFont::gfxDWriteFont(gfxFontEntry *aFontEntry,
const gfxFontStyle *aFontStyle,
bool aNeedsBold,
AntialiasOption anAAOption)
: gfxFont(aFontEntry, aFontStyle, anAAOption)
, mCairoFontFace(nullptr)
, mMetrics(nullptr)
, mSpaceGlyph(0)
, mNeedsOblique(false)
, mNeedsBold(aNeedsBold)
, mUseSubpixelPositions(false)
, mAllowManualShowGlyphs(true)
{
gfxDWriteFontEntry *fe =
static_cast<gfxDWriteFontEntry*>(aFontEntry);
nsresult rv;
DWRITE_FONT_SIMULATIONS sims = DWRITE_FONT_SIMULATIONS_NONE;
if ((GetStyle()->style != NS_FONT_STYLE_NORMAL) &&
fe->IsUpright() &&
GetStyle()->allowSyntheticStyle) {
// For this we always use the font_matrix for uniformity. Not the
// DWrite simulation.
mNeedsOblique = true;
}
if (aNeedsBold) {
sims |= DWRITE_FONT_SIMULATIONS_BOLD;
}
rv = fe->CreateFontFace(getter_AddRefs(mFontFace), sims);
if (NS_FAILED(rv)) {
mIsValid = false;
return;
}
ComputeMetrics(anAAOption);
}
gfxDWriteFont::~gfxDWriteFont()
{
if (mCairoFontFace) {
cairo_font_face_destroy(mCairoFontFace);
}
if (mScaledFont) {
cairo_scaled_font_destroy(mScaledFont);
}
delete mMetrics;
}
gfxFont*
gfxDWriteFont::CopyWithAntialiasOption(AntialiasOption anAAOption)
{
return new gfxDWriteFont(static_cast<gfxDWriteFontEntry*>(mFontEntry.get()),
&mStyle, mNeedsBold, anAAOption);
}
const gfxFont::Metrics&
gfxDWriteFont::GetHorizontalMetrics()
{
return *mMetrics;
}
bool
gfxDWriteFont::GetFakeMetricsForArialBlack(DWRITE_FONT_METRICS *aFontMetrics)
{
gfxFontStyle style(mStyle);
style.weight = 700;
bool needsBold;
gfxFontEntry* fe =
gfxPlatformFontList::PlatformFontList()->
FindFontForFamily(NS_LITERAL_STRING("Arial"), &style, needsBold);
if (!fe || fe == mFontEntry) {
return false;
}
RefPtr<gfxFont> font = fe->FindOrMakeFont(&style, needsBold);
gfxDWriteFont *dwFont = static_cast<gfxDWriteFont*>(font.get());
dwFont->mFontFace->GetMetrics(aFontMetrics);
return true;
}
void
gfxDWriteFont::ComputeMetrics(AntialiasOption anAAOption)
{
DWRITE_FONT_METRICS fontMetrics;
if (!(mFontEntry->Weight() == 900 &&
!mFontEntry->IsUserFont() &&
mFontEntry->Name().EqualsLiteral("Arial Black") &&
GetFakeMetricsForArialBlack(&fontMetrics)))
{
mFontFace->GetMetrics(&fontMetrics);
}
if (mStyle.sizeAdjust >= 0.0) {
gfxFloat aspect = (gfxFloat)fontMetrics.xHeight /
fontMetrics.designUnitsPerEm;
mAdjustedSize = mStyle.GetAdjustedSize(aspect);
} else {
mAdjustedSize = mStyle.size;
}
// Note that GetMeasuringMode depends on mAdjustedSize
if ((anAAOption == gfxFont::kAntialiasDefault &&
UsingClearType() &&
GetMeasuringMode() == DWRITE_MEASURING_MODE_NATURAL) ||
anAAOption == gfxFont::kAntialiasSubpixel)
{
mUseSubpixelPositions = true;
// note that this may be reset to FALSE if we determine that a bitmap
// strike is going to be used
}
gfxDWriteFontEntry *fe =
static_cast<gfxDWriteFontEntry*>(mFontEntry.get());
if (fe->IsCJKFont() && HasBitmapStrikeForSize(NS_lround(mAdjustedSize))) {
mAdjustedSize = NS_lround(mAdjustedSize);
mUseSubpixelPositions = false;
// if we have bitmaps, we need to tell Cairo NOT to use subpixel AA,
// to avoid the manual-subpixel codepath in cairo-d2d-surface.cpp
// which fails to render bitmap glyphs (see bug 626299).
// This option will be passed to the cairo_dwrite_scaled_font_t
// after creation.
mAllowManualShowGlyphs = false;
}
mMetrics = new gfxFont::Metrics;
::memset(mMetrics, 0, sizeof(*mMetrics));
mFUnitsConvFactor = float(mAdjustedSize / fontMetrics.designUnitsPerEm);
mMetrics->xHeight = fontMetrics.xHeight * mFUnitsConvFactor;
mMetrics->capHeight = fontMetrics.capHeight * mFUnitsConvFactor;
mMetrics->maxAscent = ceil(fontMetrics.ascent * mFUnitsConvFactor);
mMetrics->maxDescent = ceil(fontMetrics.descent * mFUnitsConvFactor);
mMetrics->maxHeight = mMetrics->maxAscent + mMetrics->maxDescent;
mMetrics->emHeight = mAdjustedSize;
mMetrics->emAscent = mMetrics->emHeight *
mMetrics->maxAscent / mMetrics->maxHeight;
mMetrics->emDescent = mMetrics->emHeight - mMetrics->emAscent;
mMetrics->maxAdvance = mAdjustedSize;
// try to get the true maxAdvance value from 'hhea'
gfxFontEntry::AutoTable hheaTable(GetFontEntry(),
TRUETYPE_TAG('h','h','e','a'));
if (hheaTable) {
uint32_t len;
const MetricsHeader* hhea =
reinterpret_cast<const MetricsHeader*>
(hb_blob_get_data(hheaTable, &len));
if (len >= sizeof(MetricsHeader)) {
mMetrics->maxAdvance =
uint16_t(hhea->advanceWidthMax) * mFUnitsConvFactor;
}
}
mMetrics->internalLeading = std::max(mMetrics->maxHeight - mMetrics->emHeight, 0.0);
mMetrics->externalLeading = ceil(fontMetrics.lineGap * mFUnitsConvFactor);
UINT32 ucs = L' ';
UINT16 glyph;
HRESULT hr = mFontFace->GetGlyphIndicesW(&ucs, 1, &glyph);
if (FAILED(hr)) {
mMetrics->spaceWidth = 0;
} else {
mSpaceGlyph = glyph;
mMetrics->spaceWidth = MeasureGlyphWidth(glyph);
}
// try to get aveCharWidth from the OS/2 table, fall back to measuring 'x'
// if the table is not available or if using hinted/pixel-snapped widths
if (mUseSubpixelPositions) {
mMetrics->aveCharWidth = 0;
gfxFontEntry::AutoTable os2Table(GetFontEntry(),
TRUETYPE_TAG('O','S','/','2'));
if (os2Table) {
uint32_t len;
const OS2Table* os2 =
reinterpret_cast<const OS2Table*>(hb_blob_get_data(os2Table, &len));
if (len >= 4) {
// Not checking against sizeof(mozilla::OS2Table) here because older
// versions of the table have different sizes; we only need the first
// two 16-bit fields here.
mMetrics->aveCharWidth =
int16_t(os2->xAvgCharWidth) * mFUnitsConvFactor;
}
}
}
if (mMetrics->aveCharWidth < 1) {
ucs = L'x';
if (SUCCEEDED(mFontFace->GetGlyphIndicesW(&ucs, 1, &glyph))) {
mMetrics->aveCharWidth = MeasureGlyphWidth(glyph);
}
if (mMetrics->aveCharWidth < 1) {
// Let's just assume the X is square.
mMetrics->aveCharWidth = fontMetrics.xHeight * mFUnitsConvFactor;
}
}
ucs = L'0';
if (SUCCEEDED(mFontFace->GetGlyphIndicesW(&ucs, 1, &glyph))) {
mMetrics->zeroOrAveCharWidth = MeasureGlyphWidth(glyph);
}
if (mMetrics->zeroOrAveCharWidth < 1) {
mMetrics->zeroOrAveCharWidth = mMetrics->aveCharWidth;
}
mMetrics->underlineOffset =
fontMetrics.underlinePosition * mFUnitsConvFactor;
mMetrics->underlineSize =
fontMetrics.underlineThickness * mFUnitsConvFactor;
mMetrics->strikeoutOffset =
fontMetrics.strikethroughPosition * mFUnitsConvFactor;
mMetrics->strikeoutSize =
fontMetrics.strikethroughThickness * mFUnitsConvFactor;
SanitizeMetrics(mMetrics, GetFontEntry()->mIsBadUnderlineFont);
#if 0
printf("Font: %p (%s) size: %f\n", this,
NS_ConvertUTF16toUTF8(GetName()).get(), mStyle.size);
printf(" emHeight: %f emAscent: %f emDescent: %f\n", mMetrics->emHeight, mMetrics->emAscent, mMetrics->emDescent);
printf(" maxAscent: %f maxDescent: %f maxAdvance: %f\n", mMetrics->maxAscent, mMetrics->maxDescent, mMetrics->maxAdvance);
printf(" internalLeading: %f externalLeading: %f\n", mMetrics->internalLeading, mMetrics->externalLeading);
printf(" spaceWidth: %f aveCharWidth: %f zeroOrAve: %f\n",
mMetrics->spaceWidth, mMetrics->aveCharWidth, mMetrics->zeroOrAveCharWidth);
printf(" xHeight: %f capHeight: %f\n", mMetrics->xHeight, mMetrics->capHeight);
printf(" uOff: %f uSize: %f stOff: %f stSize: %f\n",
mMetrics->underlineOffset, mMetrics->underlineSize, mMetrics->strikeoutOffset, mMetrics->strikeoutSize);
#endif
}
using namespace mozilla; // for AutoSwap_* types
struct EBLCHeader {
AutoSwap_PRUint32 version;
AutoSwap_PRUint32 numSizes;
};
struct SbitLineMetrics {
int8_t ascender;
int8_t descender;
uint8_t widthMax;
int8_t caretSlopeNumerator;
int8_t caretSlopeDenominator;
int8_t caretOffset;
int8_t minOriginSB;
int8_t minAdvanceSB;
int8_t maxBeforeBL;
int8_t minAfterBL;
int8_t pad1;
int8_t pad2;
};
struct BitmapSizeTable {
AutoSwap_PRUint32 indexSubTableArrayOffset;
AutoSwap_PRUint32 indexTablesSize;
AutoSwap_PRUint32 numberOfIndexSubTables;
AutoSwap_PRUint32 colorRef;
SbitLineMetrics hori;
SbitLineMetrics vert;
AutoSwap_PRUint16 startGlyphIndex;
AutoSwap_PRUint16 endGlyphIndex;
uint8_t ppemX;
uint8_t ppemY;
uint8_t bitDepth;
uint8_t flags;
};
typedef EBLCHeader EBSCHeader;
struct BitmapScaleTable {
SbitLineMetrics hori;
SbitLineMetrics vert;
uint8_t ppemX;
uint8_t ppemY;
uint8_t substitutePpemX;
uint8_t substitutePpemY;
};
bool
gfxDWriteFont::HasBitmapStrikeForSize(uint32_t aSize)
{
uint8_t *tableData;
uint32_t len;
void *tableContext;
BOOL exists;
HRESULT hr =
mFontFace->TryGetFontTable(DWRITE_MAKE_OPENTYPE_TAG('E', 'B', 'L', 'C'),
(const void**)&tableData, &len,
&tableContext, &exists);
if (FAILED(hr)) {
return false;
}
bool hasStrike = false;
// not really a loop, but this lets us use 'break' to skip out of the block
// as soon as we know the answer, and skips it altogether if the table is
// not present
while (exists) {
if (len < sizeof(EBLCHeader)) {
break;
}
const EBLCHeader *hdr = reinterpret_cast<const EBLCHeader*>(tableData);
if (hdr->version != 0x00020000) {
break;
}
uint32_t numSizes = hdr->numSizes;
if (numSizes > 0xffff) { // sanity-check, prevent overflow below
break;
}
if (len < sizeof(EBLCHeader) + numSizes * sizeof(BitmapSizeTable)) {
break;
}
const BitmapSizeTable *sizeTable =
reinterpret_cast<const BitmapSizeTable*>(hdr + 1);
for (uint32_t i = 0; i < numSizes; ++i, ++sizeTable) {
if (sizeTable->ppemX == aSize && sizeTable->ppemY == aSize) {
// we ignore a strike that contains fewer than 4 glyphs,
// as that probably indicates a font such as Courier New
// that provides bitmaps ONLY for the "shading" characters
// U+2591..2593
hasStrike = (uint16_t(sizeTable->endGlyphIndex) >=
uint16_t(sizeTable->startGlyphIndex) + 3);
break;
}
}
// if we reach here, we didn't find a strike; unconditionally break
// out of the while-loop block
break;
}
mFontFace->ReleaseFontTable(tableContext);
if (hasStrike) {
return true;
}
// if we didn't find a real strike, check if the font calls for scaling
// another bitmap to this size
hr = mFontFace->TryGetFontTable(DWRITE_MAKE_OPENTYPE_TAG('E', 'B', 'S', 'C'),
(const void**)&tableData, &len,
&tableContext, &exists);
if (FAILED(hr)) {
return false;
}
while (exists) {
if (len < sizeof(EBSCHeader)) {
break;
}
const EBSCHeader *hdr = reinterpret_cast<const EBSCHeader*>(tableData);
if (hdr->version != 0x00020000) {
break;
}
uint32_t numSizes = hdr->numSizes;
if (numSizes > 0xffff) {
break;
}
if (len < sizeof(EBSCHeader) + numSizes * sizeof(BitmapScaleTable)) {
break;
}
const BitmapScaleTable *scaleTable =
reinterpret_cast<const BitmapScaleTable*>(hdr + 1);
for (uint32_t i = 0; i < numSizes; ++i, ++scaleTable) {
if (scaleTable->ppemX == aSize && scaleTable->ppemY == aSize) {
hasStrike = true;
break;
}
}
break;
}
mFontFace->ReleaseFontTable(tableContext);
return hasStrike;
}
uint32_t
gfxDWriteFont::GetSpaceGlyph()
{
return mSpaceGlyph;
}
bool
gfxDWriteFont::SetupCairoFont(DrawTarget* aDrawTarget)
{
cairo_scaled_font_t *scaledFont = GetCairoScaledFont();
if (cairo_scaled_font_status(scaledFont) != CAIRO_STATUS_SUCCESS) {
// Don't cairo_set_scaled_font as that would propagate the error to
// the cairo_t, precluding any further drawing.
return false;
}
cairo_set_scaled_font(gfxFont::RefCairo(aDrawTarget), scaledFont);
return true;
}
bool
gfxDWriteFont::IsValid() const
{
return mFontFace != nullptr;
}
IDWriteFontFace*
gfxDWriteFont::GetFontFace()
{
return mFontFace.get();
}
cairo_font_face_t *
gfxDWriteFont::CairoFontFace()
{
if (!mCairoFontFace) {
#ifdef CAIRO_HAS_DWRITE_FONT
mCairoFontFace =
cairo_dwrite_font_face_create_for_dwrite_fontface(
((gfxDWriteFontEntry*)mFontEntry.get())->mFont, mFontFace);
#endif
}
return mCairoFontFace;
}
cairo_scaled_font_t *
gfxDWriteFont::GetCairoScaledFont()
{
if (!mScaledFont) {
cairo_matrix_t sizeMatrix;
cairo_matrix_t identityMatrix;
cairo_matrix_init_scale(&sizeMatrix, mAdjustedSize, mAdjustedSize);
cairo_matrix_init_identity(&identityMatrix);
cairo_font_options_t *fontOptions = cairo_font_options_create();
if (mNeedsOblique) {
double skewfactor = OBLIQUE_SKEW_FACTOR;
cairo_matrix_t style;
cairo_matrix_init(&style,
1, //xx
0, //yx
-1 * skewfactor, //xy
1, //yy
0, //x0
0); //y0
cairo_matrix_multiply(&sizeMatrix, &sizeMatrix, &style);
}
if (mAntialiasOption != kAntialiasDefault) {
cairo_font_options_set_antialias(fontOptions,
GetCairoAntialiasOption(mAntialiasOption));
}
mScaledFont = cairo_scaled_font_create(CairoFontFace(),
&sizeMatrix,
&identityMatrix,
fontOptions);
cairo_font_options_destroy(fontOptions);
cairo_dwrite_scaled_font_allow_manual_show_glyphs(mScaledFont,
mAllowManualShowGlyphs);
cairo_dwrite_scaled_font_set_force_GDI_classic(mScaledFont,
GetForceGDIClassic());
}
NS_ASSERTION(mAdjustedSize == 0.0 ||
cairo_scaled_font_status(mScaledFont)
== CAIRO_STATUS_SUCCESS,
"Failed to make scaled font");
return mScaledFont;
}
gfxFont::RunMetrics
gfxDWriteFont::Measure(const gfxTextRun* aTextRun,
uint32_t aStart, uint32_t aEnd,
BoundingBoxType aBoundingBoxType,
DrawTarget* aRefDrawTarget,
Spacing* aSpacing,
uint16_t aOrientation)
{
gfxFont::RunMetrics metrics =
gfxFont::Measure(aTextRun, aStart, aEnd, aBoundingBoxType,
aRefDrawTarget, aSpacing, aOrientation);
// if aBoundingBoxType is LOOSE_INK_EXTENTS
// and the underlying cairo font may be antialiased,
// we can't trust Windows to have considered all the pixels
// so we need to add "padding" to the bounds.
// (see bugs 475968, 439831, compare also bug 445087)
if (aBoundingBoxType == LOOSE_INK_EXTENTS &&
mAntialiasOption != kAntialiasNone &&
GetMeasuringMode() == DWRITE_MEASURING_MODE_GDI_CLASSIC &&
metrics.mBoundingBox.width > 0) {
metrics.mBoundingBox.x -= aTextRun->GetAppUnitsPerDevUnit();
metrics.mBoundingBox.width += aTextRun->GetAppUnitsPerDevUnit() * 3;
}
return metrics;
}
bool
gfxDWriteFont::ProvidesGlyphWidths() const
{
return !mUseSubpixelPositions ||
(mFontFace->GetSimulations() & DWRITE_FONT_SIMULATIONS_BOLD);
}
int32_t
gfxDWriteFont::GetGlyphWidth(DrawTarget& aDrawTarget, uint16_t aGID)
{
if (!mGlyphWidths) {
mGlyphWidths = MakeUnique<nsDataHashtable<nsUint32HashKey,int32_t>>(128);
}
int32_t width = -1;
if (mGlyphWidths->Get(aGID, &width)) {
return width;
}
width = NS_lround(MeasureGlyphWidth(aGID) * 65536.0);
mGlyphWidths->Put(aGID, width);
return width;
}
already_AddRefed<GlyphRenderingOptions>
gfxDWriteFont::GetGlyphRenderingOptions(const TextRunDrawParams* aRunParams)
{
if (UsingClearType()) {
return Factory::CreateDWriteGlyphRenderingOptions(
gfxWindowsPlatform::GetPlatform()->GetRenderingParams(GetForceGDIClassic() ?
gfxWindowsPlatform::TEXT_RENDERING_GDI_CLASSIC : gfxWindowsPlatform::TEXT_RENDERING_NORMAL));
} else {
return Factory::CreateDWriteGlyphRenderingOptions(gfxWindowsPlatform::GetPlatform()->
GetRenderingParams(gfxWindowsPlatform::TEXT_RENDERING_NO_CLEARTYPE));
}
}
bool
gfxDWriteFont::GetForceGDIClassic()
{
return static_cast<gfxDWriteFontEntry*>(mFontEntry.get())->GetForceGDIClassic() &&
cairo_dwrite_get_cleartype_rendering_mode() < 0 &&
GetAdjustedSize() <=
gfxDWriteFontList::PlatformFontList()->GetForceGDIClassicMaxFontSize();
}
DWRITE_MEASURING_MODE
gfxDWriteFont::GetMeasuringMode()
{
return GetForceGDIClassic()
? DWRITE_MEASURING_MODE_GDI_CLASSIC
: gfxWindowsPlatform::GetPlatform()->DWriteMeasuringMode();
}
gfxFloat
gfxDWriteFont::MeasureGlyphWidth(uint16_t aGlyph)
{
DWRITE_GLYPH_METRICS metrics;
HRESULT hr;
if (mUseSubpixelPositions) {
hr = mFontFace->GetDesignGlyphMetrics(&aGlyph, 1, &metrics, FALSE);
if (SUCCEEDED(hr)) {
return metrics.advanceWidth * mFUnitsConvFactor;
}
} else {
hr = mFontFace->GetGdiCompatibleGlyphMetrics(
FLOAT(mAdjustedSize), 1.0f, nullptr,
GetMeasuringMode() == DWRITE_MEASURING_MODE_GDI_NATURAL,
&aGlyph, 1, &metrics, FALSE);
if (SUCCEEDED(hr)) {
return NS_lround(metrics.advanceWidth * mFUnitsConvFactor);
}
}
return 0;
}
void
gfxDWriteFont::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
FontCacheSizes* aSizes) const
{
gfxFont::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
aSizes->mFontInstances += aMallocSizeOf(mMetrics);
if (mGlyphWidths) {
aSizes->mFontInstances +=
mGlyphWidths->ShallowSizeOfIncludingThis(aMallocSizeOf);
}
}
void
gfxDWriteFont::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
FontCacheSizes* aSizes) const
{
aSizes->mFontInstances += aMallocSizeOf(this);
AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
}
already_AddRefed<ScaledFont>
gfxDWriteFont::GetScaledFont(mozilla::gfx::DrawTarget *aTarget)
{
bool wantCairo = aTarget->GetBackendType() == BackendType::CAIRO;
if (mAzureScaledFont && mAzureScaledFontIsCairo == wantCairo) {
RefPtr<ScaledFont> scaledFont(mAzureScaledFont);
return scaledFont.forget();
}
NativeFont nativeFont;
nativeFont.mType = NativeFontType::DWRITE_FONT_FACE;
nativeFont.mFont = GetFontFace();
if (wantCairo) {
mAzureScaledFont = Factory::CreateScaledFontWithCairo(nativeFont,
GetAdjustedSize(),
GetCairoScaledFont());
} else if (aTarget->GetBackendType() == BackendType::SKIA) {
gfxDWriteFontEntry *fe =
static_cast<gfxDWriteFontEntry*>(mFontEntry.get());
bool useEmbeddedBitmap = (fe->IsCJKFont() && HasBitmapStrikeForSize(NS_lround(mAdjustedSize)));
const gfxFontStyle* fontStyle = GetStyle();
mAzureScaledFont =
Factory::CreateScaledFontForDWriteFont(mFontFace, fontStyle,
GetAdjustedSize(),
useEmbeddedBitmap,
GetForceGDIClassic());
} else {
mAzureScaledFont = Factory::CreateScaledFontForNativeFont(nativeFont,
GetAdjustedSize());
}
mAzureScaledFontIsCairo = wantCairo;
RefPtr<ScaledFont> scaledFont(mAzureScaledFont);
return scaledFont.forget();
}

107
gfx/thebes/gfxDWriteFonts.h Normal file
View file

@ -0,0 +1,107 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_WINDOWSDWRITEFONTS_H
#define GFX_WINDOWSDWRITEFONTS_H
#include "mozilla/MemoryReporting.h"
#include "mozilla/UniquePtr.h"
#include <dwrite.h>
#include "gfxFont.h"
#include "gfxUserFontSet.h"
#include "cairo-win32.h"
#include "nsDataHashtable.h"
#include "nsHashKeys.h"
/**
* \brief Class representing a font face for a font entry.
*/
class gfxDWriteFont : public gfxFont
{
public:
gfxDWriteFont(gfxFontEntry *aFontEntry,
const gfxFontStyle *aFontStyle,
bool aNeedsBold = false,
AntialiasOption = kAntialiasDefault);
~gfxDWriteFont();
virtual gfxFont*
CopyWithAntialiasOption(AntialiasOption anAAOption) override;
virtual uint32_t GetSpaceGlyph() override;
virtual bool SetupCairoFont(DrawTarget* aDrawTarget) override;
virtual bool AllowSubpixelAA() override
{ return mAllowManualShowGlyphs; }
bool IsValid() const;
IDWriteFontFace *GetFontFace();
/* override Measure to add padding for antialiasing */
virtual RunMetrics Measure(const gfxTextRun *aTextRun,
uint32_t aStart, uint32_t aEnd,
BoundingBoxType aBoundingBoxType,
DrawTarget *aDrawTargetForTightBoundingBox,
Spacing *aSpacing,
uint16_t aOrientation) override;
virtual bool ProvidesGlyphWidths() const override;
virtual int32_t GetGlyphWidth(DrawTarget& aDrawTarget,
uint16_t aGID) override;
virtual already_AddRefed<mozilla::gfx::GlyphRenderingOptions>
GetGlyphRenderingOptions(const TextRunDrawParams* aRunParams = nullptr) override;
virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontCacheSizes* aSizes) const override;
virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontCacheSizes* aSizes) const override;
virtual FontType GetType() const override { return FONT_TYPE_DWRITE; }
virtual already_AddRefed<mozilla::gfx::ScaledFont>
GetScaledFont(mozilla::gfx::DrawTarget *aTarget) override;
virtual cairo_scaled_font_t *GetCairoScaledFont() override;
protected:
virtual const Metrics& GetHorizontalMetrics() override;
bool GetFakeMetricsForArialBlack(DWRITE_FONT_METRICS *aFontMetrics);
void ComputeMetrics(AntialiasOption anAAOption);
bool HasBitmapStrikeForSize(uint32_t aSize);
cairo_font_face_t *CairoFontFace();
gfxFloat MeasureGlyphWidth(uint16_t aGlyph);
DWRITE_MEASURING_MODE GetMeasuringMode();
bool GetForceGDIClassic();
RefPtr<IDWriteFontFace> mFontFace;
cairo_font_face_t *mCairoFontFace;
Metrics *mMetrics;
// cache of glyph widths in 16.16 fixed-point pixels
mozilla::UniquePtr<nsDataHashtable<nsUint32HashKey,int32_t>> mGlyphWidths;
uint32_t mSpaceGlyph;
bool mNeedsOblique;
bool mNeedsBold;
bool mUseSubpixelPositions;
bool mAllowManualShowGlyphs;
bool mAzureScaledFontIsCairo;
};
#endif

254
gfx/thebes/gfxDrawable.cpp Normal file
View file

@ -0,0 +1,254 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gfxDrawable.h"
#include "gfxASurface.h"
#include "gfxContext.h"
#include "gfxPlatform.h"
#include "gfx2DGlue.h"
#ifdef MOZ_X11
#include "cairo.h"
#include "gfxXlibSurface.h"
#endif
#include "mozilla/gfx/Logging.h"
using namespace mozilla;
using namespace mozilla::gfx;
gfxSurfaceDrawable::gfxSurfaceDrawable(SourceSurface* aSurface,
const IntSize aSize,
const gfxMatrix aTransform)
: gfxDrawable(aSize)
, mSourceSurface(aSurface)
, mTransform(aTransform)
{
if (!mSourceSurface) {
gfxWarning() << "Creating gfxSurfaceDrawable with null SourceSurface";
}
}
bool
gfxSurfaceDrawable::DrawWithSamplingRect(DrawTarget* aDrawTarget,
CompositionOp aOp,
AntialiasMode aAntialiasMode,
const gfxRect& aFillRect,
const gfxRect& aSamplingRect,
ExtendMode aExtendMode,
const SamplingFilter aSamplingFilter,
gfxFloat aOpacity)
{
if (!mSourceSurface) {
return true;
}
// When drawing with CLAMP we can expand the sampling rect to the nearest pixel
// without changing the result.
IntRect intRect = IntRect::RoundOut(aSamplingRect.x, aSamplingRect.y,
aSamplingRect.width, aSamplingRect.height);
IntSize size = mSourceSurface->GetSize();
if (!IntRect(IntPoint(), size).Contains(intRect)) {
return false;
}
DrawInternal(aDrawTarget, aOp, aAntialiasMode, aFillRect, intRect,
ExtendMode::CLAMP, aSamplingFilter, aOpacity, gfxMatrix());
return true;
}
bool
gfxSurfaceDrawable::Draw(gfxContext* aContext,
const gfxRect& aFillRect,
ExtendMode aExtendMode,
const SamplingFilter aSamplingFilter,
gfxFloat aOpacity,
const gfxMatrix& aTransform)
{
if (!mSourceSurface) {
return true;
}
DrawInternal(aContext->GetDrawTarget(), aContext->CurrentOp(),
aContext->CurrentAntialiasMode(), aFillRect, IntRect(),
aExtendMode, aSamplingFilter, aOpacity, aTransform);
return true;
}
void
gfxSurfaceDrawable::DrawInternal(DrawTarget* aDrawTarget,
CompositionOp aOp,
AntialiasMode aAntialiasMode,
const gfxRect& aFillRect,
const IntRect& aSamplingRect,
ExtendMode aExtendMode,
const SamplingFilter aSamplingFilter,
gfxFloat aOpacity,
const gfxMatrix& aTransform)
{
Matrix patternTransform = ToMatrix(aTransform * mTransform);
patternTransform.Invert();
SurfacePattern pattern(mSourceSurface, aExtendMode,
patternTransform, aSamplingFilter, aSamplingRect);
Rect fillRect = ToRect(aFillRect);
if (aOp == CompositionOp::OP_SOURCE && aOpacity == 1.0) {
// Emulate cairo operator source which is bound by mask!
aDrawTarget->ClearRect(fillRect);
aDrawTarget->FillRect(fillRect, pattern);
} else {
aDrawTarget->FillRect(fillRect, pattern,
DrawOptions(aOpacity, aOp, aAntialiasMode));
}
}
gfxCallbackDrawable::gfxCallbackDrawable(gfxDrawingCallback* aCallback,
const IntSize aSize)
: gfxDrawable(aSize)
, mCallback(aCallback)
{
}
already_AddRefed<gfxSurfaceDrawable>
gfxCallbackDrawable::MakeSurfaceDrawable(const SamplingFilter aSamplingFilter)
{
SurfaceFormat format =
gfxPlatform::GetPlatform()->Optimal2DFormatForContent(gfxContentType::COLOR_ALPHA);
RefPtr<DrawTarget> dt =
gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(mSize,
format);
if (!dt || !dt->IsValid())
return nullptr;
RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(dt);
MOZ_ASSERT(ctx); // already checked for target above
Draw(ctx, gfxRect(0, 0, mSize.width, mSize.height), ExtendMode::CLAMP,
aSamplingFilter);
RefPtr<SourceSurface> surface = dt->Snapshot();
if (surface) {
RefPtr<gfxSurfaceDrawable> drawable = new gfxSurfaceDrawable(surface, mSize);
return drawable.forget();
}
return nullptr;
}
static bool
IsRepeatingExtendMode(ExtendMode aExtendMode)
{
switch (aExtendMode) {
case ExtendMode::REPEAT:
case ExtendMode::REPEAT_X:
case ExtendMode::REPEAT_Y:
return true;
default:
return false;
}
}
bool
gfxCallbackDrawable::Draw(gfxContext* aContext,
const gfxRect& aFillRect,
ExtendMode aExtendMode,
const SamplingFilter aSamplingFilter,
gfxFloat aOpacity,
const gfxMatrix& aTransform)
{
if ((IsRepeatingExtendMode(aExtendMode) || aOpacity != 1.0 || aContext->CurrentOp() != CompositionOp::OP_OVER) &&
!mSurfaceDrawable) {
mSurfaceDrawable = MakeSurfaceDrawable(aSamplingFilter);
}
if (mSurfaceDrawable)
return mSurfaceDrawable->Draw(aContext, aFillRect, aExtendMode,
aSamplingFilter,
aOpacity, aTransform);
if (mCallback)
return (*mCallback)(aContext, aFillRect, aSamplingFilter, aTransform);
return false;
}
gfxPatternDrawable::gfxPatternDrawable(gfxPattern* aPattern,
const IntSize aSize)
: gfxDrawable(aSize)
, mPattern(aPattern)
{
}
gfxPatternDrawable::~gfxPatternDrawable()
{
}
class DrawingCallbackFromDrawable : public gfxDrawingCallback {
public:
explicit DrawingCallbackFromDrawable(gfxDrawable* aDrawable)
: mDrawable(aDrawable) {
NS_ASSERTION(aDrawable, "aDrawable is null!");
}
virtual ~DrawingCallbackFromDrawable() {}
virtual bool operator()(gfxContext* aContext,
const gfxRect& aFillRect,
const SamplingFilter aSamplingFilter,
const gfxMatrix& aTransform = gfxMatrix())
{
return mDrawable->Draw(aContext, aFillRect, ExtendMode::CLAMP,
aSamplingFilter, 1.0,
aTransform);
}
private:
RefPtr<gfxDrawable> mDrawable;
};
already_AddRefed<gfxCallbackDrawable>
gfxPatternDrawable::MakeCallbackDrawable()
{
RefPtr<gfxDrawingCallback> callback =
new DrawingCallbackFromDrawable(this);
RefPtr<gfxCallbackDrawable> callbackDrawable =
new gfxCallbackDrawable(callback, mSize);
return callbackDrawable.forget();
}
bool
gfxPatternDrawable::Draw(gfxContext* aContext,
const gfxRect& aFillRect,
ExtendMode aExtendMode,
const SamplingFilter aSamplingFilter,
gfxFloat aOpacity,
const gfxMatrix& aTransform)
{
DrawTarget& aDrawTarget = *aContext->GetDrawTarget();
if (!mPattern)
return false;
if (IsRepeatingExtendMode(aExtendMode)) {
// We can't use mPattern directly: We want our repeated tiles to have
// the size mSize, which might not be the case in mPattern.
// So we need to draw mPattern into a surface of size mSize, create
// a pattern from the surface and draw that pattern.
// gfxCallbackDrawable and gfxSurfaceDrawable already know how to do
// those things, so we use them here. Drawing mPattern into the surface
// will happen through this Draw() method with aRepeat = false.
RefPtr<gfxCallbackDrawable> callbackDrawable = MakeCallbackDrawable();
return callbackDrawable->Draw(aContext, aFillRect, aExtendMode,
aSamplingFilter,
aOpacity, aTransform);
}
gfxMatrix oldMatrix = mPattern->GetMatrix();
mPattern->SetMatrix(aTransform * oldMatrix);
DrawOptions drawOptions(aOpacity);
aDrawTarget.FillRect(ToRect(aFillRect),
*mPattern->GetPattern(&aDrawTarget), drawOptions);
mPattern->SetMatrix(oldMatrix);
return true;
}

182
gfx/thebes/gfxDrawable.h Normal file
View file

@ -0,0 +1,182 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_DRAWABLE_H
#define GFX_DRAWABLE_H
#include "gfxRect.h"
#include "gfxMatrix.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/Types.h"
class gfxContext;
class gfxPattern;
/**
* gfxDrawable
* An Interface representing something that has an intrinsic size and can draw
* itself repeatedly.
*/
class gfxDrawable {
NS_INLINE_DECL_REFCOUNTING(gfxDrawable)
public:
typedef mozilla::gfx::AntialiasMode AntialiasMode;
typedef mozilla::gfx::CompositionOp CompositionOp;
typedef mozilla::gfx::DrawTarget DrawTarget;
explicit gfxDrawable(const mozilla::gfx::IntSize aSize)
: mSize(aSize) {}
/**
* Draw into aContext filling aFillRect, possibly repeating, using aSamplingFilter.
* aTransform is a userspace to "image"space matrix. For example, if Draw
* draws using a gfxPattern, this is the matrix that should be set on the
* pattern prior to rendering it.
* @return whether drawing was successful
*/
virtual bool Draw(gfxContext* aContext,
const gfxRect& aFillRect,
mozilla::gfx::ExtendMode aExtendMode,
const mozilla::gfx::SamplingFilter aSamplingFilter,
gfxFloat aOpacity = 1.0,
const gfxMatrix& aTransform = gfxMatrix()) = 0;
virtual bool DrawWithSamplingRect(DrawTarget* aDrawTarget,
CompositionOp aOp,
AntialiasMode aAntialiasMode,
const gfxRect& aFillRect,
const gfxRect& aSamplingRect,
mozilla::gfx::ExtendMode aExtendMode,
const mozilla::gfx::SamplingFilter aSamplingFilter,
gfxFloat aOpacity = 1.0)
{
return false;
}
virtual mozilla::gfx::IntSize Size() { return mSize; }
protected:
// Protected destructor, to discourage deletion outside of Release():
virtual ~gfxDrawable() {}
const mozilla::gfx::IntSize mSize;
};
/**
* gfxSurfaceDrawable
* A convenience implementation of gfxDrawable for surfaces.
*/
class gfxSurfaceDrawable : public gfxDrawable {
public:
gfxSurfaceDrawable(mozilla::gfx::SourceSurface* aSurface, const mozilla::gfx::IntSize aSize,
const gfxMatrix aTransform = gfxMatrix());
virtual ~gfxSurfaceDrawable() {}
virtual bool Draw(gfxContext* aContext,
const gfxRect& aFillRect,
mozilla::gfx::ExtendMode aExtendMode,
const mozilla::gfx::SamplingFilter aSamplingFilter,
gfxFloat aOpacity = 1.0,
const gfxMatrix& aTransform = gfxMatrix());
virtual bool DrawWithSamplingRect(DrawTarget* aDrawTarget,
CompositionOp aOp,
AntialiasMode aAntialiasMode,
const gfxRect& aFillRect,
const gfxRect& aSamplingRect,
mozilla::gfx::ExtendMode aExtendMode,
const mozilla::gfx::SamplingFilter aSamplingFilter,
gfxFloat aOpacity = 1.0);
protected:
void DrawInternal(DrawTarget* aDrawTarget,
CompositionOp aOp,
AntialiasMode aAntialiasMode,
const gfxRect& aFillRect,
const mozilla::gfx::IntRect& aSamplingRect,
mozilla::gfx::ExtendMode aExtendMode,
const mozilla::gfx::SamplingFilter aSamplingFilter,
gfxFloat aOpacity,
const gfxMatrix& aTransform = gfxMatrix());
RefPtr<mozilla::gfx::SourceSurface> mSourceSurface;
const gfxMatrix mTransform;
};
/**
* gfxDrawingCallback
* A simple drawing functor.
*/
class gfxDrawingCallback {
NS_INLINE_DECL_REFCOUNTING(gfxDrawingCallback)
protected:
// Protected destructor, to discourage deletion outside of Release():
virtual ~gfxDrawingCallback() {}
public:
/**
* Draw into aContext filling aFillRect using aSamplingFilter.
* aTransform is a userspace to "image"space matrix. For example, if Draw
* draws using a gfxPattern, this is the matrix that should be set on the
* pattern prior to rendering it.
* @return whether drawing was successful
*/
virtual bool operator()(gfxContext* aContext,
const gfxRect& aFillRect,
const mozilla::gfx::SamplingFilter aSamplingFilter,
const gfxMatrix& aTransform = gfxMatrix()) = 0;
};
/**
* gfxCallbackDrawable
* A convenience implementation of gfxDrawable for callbacks.
*/
class gfxCallbackDrawable : public gfxDrawable {
public:
gfxCallbackDrawable(gfxDrawingCallback* aCallback, const mozilla::gfx::IntSize aSize);
virtual ~gfxCallbackDrawable() {}
virtual bool Draw(gfxContext* aContext,
const gfxRect& aFillRect,
mozilla::gfx::ExtendMode aExtendMode,
const mozilla::gfx::SamplingFilter aSamplingFilter,
gfxFloat aOpacity = 1.0,
const gfxMatrix& aTransform = gfxMatrix());
protected:
already_AddRefed<gfxSurfaceDrawable>
MakeSurfaceDrawable(mozilla::gfx::SamplingFilter aSamplingFilter =
mozilla::gfx::SamplingFilter::LINEAR);
RefPtr<gfxDrawingCallback> mCallback;
RefPtr<gfxSurfaceDrawable> mSurfaceDrawable;
};
/**
* gfxPatternDrawable
* A convenience implementation of gfxDrawable for patterns.
*/
class gfxPatternDrawable : public gfxDrawable {
public:
gfxPatternDrawable(gfxPattern* aPattern,
const mozilla::gfx::IntSize aSize);
virtual ~gfxPatternDrawable();
virtual bool Draw(gfxContext* aContext,
const gfxRect& aFillRect,
mozilla::gfx::ExtendMode aExtendMode,
const mozilla::gfx::SamplingFilter aSamplingFilter,
gfxFloat aOpacity = 1.0,
const gfxMatrix& aTransform = gfxMatrix());
protected:
already_AddRefed<gfxCallbackDrawable> MakeCallbackDrawable();
RefPtr<gfxPattern> mPattern;
};
#endif /* GFX_DRAWABLE_H */

123
gfx/thebes/gfxEnv.h Normal file
View file

@ -0,0 +1,123 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_ENV_H
#define GFX_ENV_H
#include "prenv.h"
// To register the check for an environment variable existence (and not empty),
// add a line in this file using the DECL_GFX_ENV macro.
//
// For example this line in the .h:
// DECL_GFX_ENV("MOZ_DISABLE_CONTEXT_SHARING_GLX",DisableContextSharingGLX);
// means that you can call
// bool var = gfxEnv::DisableContextSharingGLX();
// and that the value will be checked only once, first time we call it, then cached.
#define DECL_GFX_ENV(Env, Name) \
static bool Name() { \
static bool isSet = IsEnvSet(Env);\
return isSet; \
}
class gfxEnv final
{
public:
// This is where DECL_GFX_ENV for each of the environment variables should go.
// We will keep these in an alphabetical order by the environment variable,
// to make it easier to see if a method accessing an entry already exists.
// Just insert yours in the list.
// Debugging inside of ContainerLayerComposite
DECL_GFX_ENV("DUMP_DEBUG", DumpDebug);
// OpenGL shader debugging in OGLShaderProgram, in DEBUG only
DECL_GFX_ENV("MOZ_DEBUG_SHADERS", DebugShaders);
// Disabling context sharing in GLContextProviderGLX
DECL_GFX_ENV("MOZ_DISABLE_CONTEXT_SHARING_GLX", DisableContextSharingGlx);
// Disabling the crash guard in DriverCrashGuard
DECL_GFX_ENV("MOZ_DISABLE_CRASH_GUARD", DisableCrashGuard);
DECL_GFX_ENV("MOZ_FORCE_CRASH_GUARD_NIGHTLY", ForceCrashGuardNightly);
// We force present to work around some Windows bugs - disable that if this
// environment variable is set.
DECL_GFX_ENV("MOZ_DISABLE_FORCE_PRESENT", DisableForcePresent);
// Together with paint dumping, only when MOZ_DUMP_PAINTING is defined.
// Dumping compositor textures is broken pretty badly. For example,
// on Linux it crashes TextureHost::GetAsSurface() returns null.
// Expect to have to fix things like this if you turn it on.
// Meanwhile, content-side texture dumping
// (conditioned on DebugDumpPainting()) is a good replacement.
DECL_GFX_ENV("MOZ_DUMP_COMPOSITOR_TEXTURES", DumpCompositorTextures);
// Dumping the layer list in LayerSorter
DECL_GFX_ENV("MOZ_DUMP_LAYER_SORT_LIST", DumpLayerSortList);
// Paint dumping, only when MOZ_DUMP_PAINTING is defined.
DECL_GFX_ENV("MOZ_DUMP_PAINT", DumpPaint);
DECL_GFX_ENV("MOZ_DUMP_PAINT_INTERMEDIATE", DumpPaintIntermediate);
DECL_GFX_ENV("MOZ_DUMP_PAINT_ITEMS", DumpPaintItems);
DECL_GFX_ENV("MOZ_DUMP_PAINT_TO_FILE", DumpPaintToFile);
// Force double buffering in ContentClient
DECL_GFX_ENV("MOZ_FORCE_DOUBLE_BUFFERING", ForceDoubleBuffering);
// Force gfxDevCrash to use MOZ_CRASH in Beta and Release
DECL_GFX_ENV("MOZ_GFX_CRASH_MOZ_CRASH", GfxDevCrashMozCrash);
// Force gfxDevCrash to use telemetry in Nightly and Aurora
DECL_GFX_ENV("MOZ_GFX_CRASH_TELEMETRY", GfxDevCrashTelemetry);
DECL_GFX_ENV("MOZ_GFX_VR_NO_DISTORTION", VRNoDistortion);
// Debugging in GLContext
DECL_GFX_ENV("MOZ_GL_DEBUG", GlDebug);
DECL_GFX_ENV("MOZ_GL_DEBUG_VERBOSE", GlDebugVerbose);
DECL_GFX_ENV("MOZ_GL_DEBUG_ABORT_ON_ERROR", GlDebugAbortOnError);
// Count GL extensions
DECL_GFX_ENV("MOZ_GL_DUMP_EXTS", GlDumpExtensions);
// Very noisy GLContext and GLContextProviderELG
DECL_GFX_ENV("MOZ_GL_SPEW", GlSpew);
// Do extra work before and after each GLX call in GLContextProviderGLX
DECL_GFX_ENV("MOZ_GLX_DEBUG", GlxDebug);
// Use X compositing
DECL_GFX_ENV("MOZ_LAYERS_ENABLE_XLIB_SURFACES", LayersEnableXlibSurfaces);
// GL compositing on Windows
DECL_GFX_ENV("MOZ_LAYERS_PREFER_EGL", LayersPreferEGL);
// Offscreen GL context for main layer manager
DECL_GFX_ENV("MOZ_LAYERS_PREFER_OFFSCREEN", LayersPreferOffscreen);
// Stop the VR rendering
DECL_GFX_ENV("NO_VR_RENDERING", NoVRRendering);
// WARNING:
// Please make sure that you've added your new envvar to the list above in
// alphabetical order. Please do not just append it to the end of the list.
private:
// Helper function, can be re-used in the other macros
static bool IsEnvSet(const char* aName) {
const char* val = PR_GetEnv(aName);
return (val != 0 && *val != '\0');
}
gfxEnv() {};
~gfxEnv() {};
gfxEnv(const gfxEnv&) = delete;
gfxEnv& operator=(const gfxEnv&) = delete;
};
#undef DECL_GFX_ENV
#endif /* GFX_ENV_H */

View file

@ -0,0 +1,217 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gfxFT2FontBase.h"
#include "gfxFT2Utils.h"
#include "harfbuzz/hb.h"
#include "mozilla/Likely.h"
#include "gfxFontConstants.h"
#include "gfxFontUtils.h"
using namespace mozilla::gfx;
gfxFT2FontBase::gfxFT2FontBase(cairo_scaled_font_t *aScaledFont,
gfxFontEntry *aFontEntry,
const gfxFontStyle *aFontStyle)
: gfxFont(aFontEntry, aFontStyle, kAntialiasDefault, aScaledFont),
mSpaceGlyph(0),
mHasMetrics(false)
{
cairo_scaled_font_reference(mScaledFont);
gfxFT2LockedFace face(this);
mFUnitsConvFactor = face.XScale();
}
gfxFT2FontBase::~gfxFT2FontBase()
{
cairo_scaled_font_destroy(mScaledFont);
}
uint32_t
gfxFT2FontBase::GetGlyph(uint32_t aCharCode)
{
// FcFreeTypeCharIndex needs to lock the FT_Face and can end up searching
// through all the postscript glyph names in the font. Therefore use a
// lightweight cache, which is stored on the cairo_font_face_t.
cairo_font_face_t *face =
cairo_scaled_font_get_font_face(CairoScaledFont());
if (cairo_font_face_status(face) != CAIRO_STATUS_SUCCESS)
return 0;
// This cache algorithm and size is based on what is done in
// cairo_scaled_font_text_to_glyphs and pango_fc_font_real_get_glyph. I
// think the concept is that adjacent characters probably come mostly from
// one Unicode block. This assumption is probably not so valid with
// scripts with large character sets as used for East Asian languages.
struct CmapCacheSlot {
uint32_t mCharCode;
uint32_t mGlyphIndex;
};
const uint32_t kNumSlots = 256;
static cairo_user_data_key_t sCmapCacheKey;
CmapCacheSlot *slots = static_cast<CmapCacheSlot*>
(cairo_font_face_get_user_data(face, &sCmapCacheKey));
if (!slots) {
// cairo's caches can keep some cairo_font_faces alive past our last
// destroy, so the destroy function (free) for the cache must be
// callable from cairo without any assumptions about what other
// modules have not been shutdown.
slots = static_cast<CmapCacheSlot*>
(calloc(kNumSlots, sizeof(CmapCacheSlot)));
if (!slots)
return 0;
cairo_status_t status =
cairo_font_face_set_user_data(face, &sCmapCacheKey, slots, free);
if (status != CAIRO_STATUS_SUCCESS) { // OOM
free(slots);
return 0;
}
// Invalidate slot 0 by setting its char code to something that would
// never end up in slot 0. All other slots are already invalid
// because they have mCharCode = 0 and a glyph for char code 0 will
// always be in the slot 0.
slots[0].mCharCode = 1;
}
CmapCacheSlot *slot = &slots[aCharCode % kNumSlots];
if (slot->mCharCode != aCharCode) {
slot->mCharCode = aCharCode;
slot->mGlyphIndex = gfxFT2LockedFace(this).GetGlyph(aCharCode);
}
return slot->mGlyphIndex;
}
void
gfxFT2FontBase::GetGlyphExtents(uint32_t aGlyph, cairo_text_extents_t* aExtents)
{
NS_PRECONDITION(aExtents != nullptr, "aExtents must not be NULL");
cairo_glyph_t glyphs[1];
glyphs[0].index = aGlyph;
glyphs[0].x = 0.0;
glyphs[0].y = 0.0;
// cairo does some caching for us here but perhaps a small gain could be
// made by caching more. It is usually only the advance that is needed,
// so caching only the advance could allow many requests to be cached with
// little memory use. Ideally this cache would be merged with
// gfxGlyphExtents.
cairo_scaled_font_glyph_extents(CairoScaledFont(), glyphs, 1, aExtents);
}
const gfxFont::Metrics&
gfxFT2FontBase::GetHorizontalMetrics()
{
if (mHasMetrics)
return mMetrics;
if (MOZ_UNLIKELY(GetStyle()->size <= 0.0) ||
MOZ_UNLIKELY(GetStyle()->sizeAdjust == 0.0)) {
new(&mMetrics) gfxFont::Metrics(); // zero initialize
mSpaceGlyph = GetGlyph(' ');
} else {
gfxFT2LockedFace face(this);
face.GetMetrics(&mMetrics, &mSpaceGlyph);
}
SanitizeMetrics(&mMetrics, false);
#if 0
// printf("font name: %s %f\n", NS_ConvertUTF16toUTF8(GetName()).get(), GetStyle()->size);
// printf ("pango font %s\n", pango_font_description_to_string (pango_font_describe (font)));
fprintf (stderr, "Font: %s\n", NS_ConvertUTF16toUTF8(GetName()).get());
fprintf (stderr, " emHeight: %f emAscent: %f emDescent: %f\n", mMetrics.emHeight, mMetrics.emAscent, mMetrics.emDescent);
fprintf (stderr, " maxAscent: %f maxDescent: %f\n", mMetrics.maxAscent, mMetrics.maxDescent);
fprintf (stderr, " internalLeading: %f externalLeading: %f\n", mMetrics.externalLeading, mMetrics.internalLeading);
fprintf (stderr, " spaceWidth: %f aveCharWidth: %f xHeight: %f\n", mMetrics.spaceWidth, mMetrics.aveCharWidth, mMetrics.xHeight);
fprintf (stderr, " uOff: %f uSize: %f stOff: %f stSize: %f\n", mMetrics.underlineOffset, mMetrics.underlineSize, mMetrics.strikeoutOffset, mMetrics.strikeoutSize);
#endif
mHasMetrics = true;
return mMetrics;
}
// Get the glyphID of a space
uint32_t
gfxFT2FontBase::GetSpaceGlyph()
{
GetHorizontalMetrics();
return mSpaceGlyph;
}
uint32_t
gfxFT2FontBase::GetGlyph(uint32_t unicode, uint32_t variation_selector)
{
if (variation_selector) {
uint32_t id =
gfxFT2LockedFace(this).GetUVSGlyph(unicode, variation_selector);
if (id) {
return id;
}
unicode = gfxFontUtils::GetUVSFallback(unicode, variation_selector);
if (unicode) {
return GetGlyph(unicode);
}
return 0;
}
return GetGlyph(unicode);
}
int32_t
gfxFT2FontBase::GetGlyphWidth(DrawTarget& aDrawTarget, uint16_t aGID)
{
cairo_text_extents_t extents;
GetGlyphExtents(aGID, &extents);
// convert to 16.16 fixed point
return NS_lround(0x10000 * extents.x_advance);
}
bool
gfxFT2FontBase::SetupCairoFont(DrawTarget* aDrawTarget)
{
// The scaled font ctm is not relevant right here because
// cairo_set_scaled_font does not record the scaled font itself, but
// merely the font_face, font_matrix, font_options. The scaled_font used
// for the target can be different from the scaled_font passed to
// cairo_set_scaled_font. (Unfortunately we have measured only for an
// identity ctm.)
cairo_scaled_font_t *cairoFont = CairoScaledFont();
if (cairo_scaled_font_status(cairoFont) != CAIRO_STATUS_SUCCESS) {
// Don't cairo_set_scaled_font as that would propagate the error to
// the cairo_t, precluding any further drawing.
return false;
}
// Thoughts on which font_options to set on the context:
//
// cairoFont has been created for screen rendering.
//
// When the context is being used for screen rendering, we should set
// font_options such that the same scaled_font gets used (when the ctm is
// the same). The use of explicit font_options recorded in
// CreateScaledFont ensures that this will happen.
//
// XXXkt: For pdf and ps surfaces, I don't know whether it's better to
// remove surface-specific options, or try to draw with the same
// scaled_font that was used to measure. As the same font_face is being
// used, its font_options will often override some values anyway (unless
// perhaps we remove those from the FcPattern at face creation).
//
// I can't see any significant difference in printing, irrespective of
// what is set here. It's too late to change things here as measuring has
// already taken place. We should really be measuring with a different
// font for pdf and ps surfaces (bug 403513).
cairo_set_scaled_font(gfxFont::RefCairo(aDrawTarget), cairoFont);
return true;
}

View file

@ -0,0 +1,45 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_FT2FONTBASE_H
#define GFX_FT2FONTBASE_H
#include "cairo.h"
#include "gfxContext.h"
#include "gfxFont.h"
#include "mozilla/gfx/2D.h"
class gfxFT2FontBase : public gfxFont {
public:
gfxFT2FontBase(cairo_scaled_font_t *aScaledFont,
gfxFontEntry *aFontEntry,
const gfxFontStyle *aFontStyle);
virtual ~gfxFT2FontBase();
uint32_t GetGlyph(uint32_t aCharCode);
void GetGlyphExtents(uint32_t aGlyph,
cairo_text_extents_t* aExtents);
virtual uint32_t GetSpaceGlyph() override;
virtual bool ProvidesGetGlyph() const override { return true; }
virtual uint32_t GetGlyph(uint32_t unicode,
uint32_t variation_selector) override;
virtual bool ProvidesGlyphWidths() const override { return true; }
virtual int32_t GetGlyphWidth(DrawTarget& aDrawTarget,
uint16_t aGID) override;
cairo_scaled_font_t *CairoScaledFont() { return mScaledFont; };
virtual bool SetupCairoFont(DrawTarget* aDrawTarget) override;
virtual FontType GetType() const override { return FONT_TYPE_FT2; }
protected:
virtual const Metrics& GetHorizontalMetrics() override;
uint32_t mSpaceGlyph;
bool mHasMetrics;
Metrics mMetrics;
};
#endif /* GFX_FT2FONTBASE_H */

File diff suppressed because it is too large Load diff

200
gfx/thebes/gfxFT2FontList.h Normal file
View file

@ -0,0 +1,200 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_FT2FONTLIST_H
#define GFX_FT2FONTLIST_H
#include "mozilla/MemoryReporting.h"
#include "gfxPlatformFontList.h"
namespace mozilla {
namespace dom {
class FontListEntry;
};
};
using mozilla::dom::FontListEntry;
class FontNameCache;
typedef struct FT_FaceRec_* FT_Face;
class nsZipArchive;
class FT2FontEntry : public gfxFontEntry
{
public:
FT2FontEntry(const nsAString& aFaceName) :
gfxFontEntry(aFaceName),
mFTFace(nullptr),
mFontFace(nullptr),
mFTFontIndex(0)
{
}
~FT2FontEntry();
const nsString& GetName() const {
return Name();
}
// create a font entry for a downloaded font
static FT2FontEntry*
CreateFontEntry(const nsAString& aFontName,
uint16_t aWeight,
int16_t aStretch,
uint8_t aStyle,
const uint8_t* aFontData,
uint32_t aLength);
// create a font entry representing an installed font, identified by
// a FontListEntry; the freetype and cairo faces will not be instantiated
// until actually needed
static FT2FontEntry*
CreateFontEntry(const FontListEntry& aFLE);
// Create a font entry for a given freetype face; if it is an installed font,
// also record the filename and index.
// aFontData (if non-nullptr) is NS_Malloc'ed data that aFace depends on,
// to be freed after the face is destroyed
static FT2FontEntry*
CreateFontEntry(FT_Face aFace,
const char *aFilename, uint8_t aIndex,
const nsAString& aName,
const uint8_t* aFontData = nullptr);
virtual gfxFont *CreateFontInstance(const gfxFontStyle *aFontStyle,
bool aNeedsBold) override;
// Create (if necessary) and return the cairo_font_face for this font.
// This may fail and return null, so caller must be prepared to handle this.
cairo_font_face_t *CairoFontFace();
// Create a cairo_scaled_font for this face, with the given style.
// This may fail and return null, so caller must be prepared to handle this.
cairo_scaled_font_t *CreateScaledFont(const gfxFontStyle *aStyle);
nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr) override;
virtual hb_blob_t* GetFontTable(uint32_t aTableTag) override;
virtual nsresult CopyFontTable(uint32_t aTableTag,
nsTArray<uint8_t>& aBuffer) override;
// Check for various kinds of brokenness, and set flags on the entry
// accordingly so that we avoid using bad font tables
void CheckForBrokenFont(gfxFontFamily *aFamily);
virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontListSizes* aSizes) const override;
virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontListSizes* aSizes) const override;
FT_Face mFTFace;
cairo_font_face_t *mFontFace;
nsCString mFilename;
uint8_t mFTFontIndex;
};
class FT2FontFamily : public gfxFontFamily
{
public:
// Flags to indicate whether a font should be "visible" in the global
// font list (available for use in font-family), or "hidden" (available
// only to support a matching data: URI used in @font-face).
typedef enum {
kVisible,
kHidden
} Visibility;
FT2FontFamily(const nsAString& aName) :
gfxFontFamily(aName) { }
// Append this family's faces to the IPC fontlist
void AddFacesToFontList(InfallibleTArray<FontListEntry>* aFontList,
Visibility aVisibility);
};
class gfxFT2FontList : public gfxPlatformFontList
{
public:
gfxFT2FontList();
virtual ~gfxFT2FontList();
virtual gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
uint16_t aWeight,
int16_t aStretch,
uint8_t aStyle);
virtual gfxFontEntry* MakePlatformFont(const nsAString& aFontName,
uint16_t aWeight,
int16_t aStretch,
uint8_t aStyle,
const uint8_t* aFontData,
uint32_t aLength);
void GetSystemFontList(InfallibleTArray<FontListEntry>* retValue);
static gfxFT2FontList* PlatformFontList() {
return static_cast<gfxFT2FontList*>(gfxPlatformFontList::PlatformFontList());
}
virtual void GetFontFamilyList(nsTArray<RefPtr<gfxFontFamily> >& aFamilyArray);
void WillShutdown();
protected:
typedef enum {
kUnknown,
kStandard
} StandardFile;
// initialize font lists
virtual nsresult InitFontListForPlatform() override;
void AppendFaceFromFontListEntry(const FontListEntry& aFLE,
StandardFile aStdFile);
void AppendFacesFromFontFile(const nsCString& aFileName,
FontNameCache *aCache,
StandardFile aStdFile,
FT2FontFamily::Visibility aVisibility);
void AppendFacesFromOmnijarEntry(nsZipArchive *aReader,
const nsCString& aEntryName,
FontNameCache *aCache,
bool aJarChanged);
// the defaults here are suitable for reading bundled fonts from omnijar
void AppendFacesFromCachedFaceList(const nsCString& aFileName,
const nsCString& aFaceList,
StandardFile aStdFile = kStandard,
FT2FontFamily::Visibility aVisibility =
FT2FontFamily::kVisible);
void AddFaceToList(const nsCString& aEntryName, uint32_t aIndex,
StandardFile aStdFile,
FT2FontFamily::Visibility aVisibility,
FT_Face aFace, nsCString& aFaceList);
void FindFonts();
void FindFontsInOmnijar(FontNameCache *aCache);
void FindFontsInDir(const nsCString& aDir, FontNameCache* aFNC,
FT2FontFamily::Visibility aVisibility);
virtual gfxFontFamily*
GetDefaultFontForPlatform(const gfxFontStyle* aStyle) override;
nsTHashtable<nsStringHashKey> mSkipSpaceLookupCheckFamilies;
private:
FontFamilyTable mHiddenFontFamilies;
mozilla::UniquePtr<FontNameCache> mFontNameCache;
int64_t mJarModifiedTime;
nsCOMPtr<nsIObserver> mObserver;
};
#endif /* GFX_FT2FONTLIST_H */

227
gfx/thebes/gfxFT2Fonts.cpp Normal file
View file

@ -0,0 +1,227 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#if defined(MOZ_WIDGET_GTK)
#include "gfxPlatformGtk.h"
#define gfxToolkitPlatform gfxPlatformGtk
#elif defined(XP_WIN)
#include "gfxWindowsPlatform.h"
#define gfxToolkitPlatform gfxWindowsPlatform
#elif defined(ANDROID)
#include "gfxAndroidPlatform.h"
#define gfxToolkitPlatform gfxAndroidPlatform
#endif
#include "gfxTypes.h"
#include "gfxFT2Fonts.h"
#include "gfxFT2FontBase.h"
#include "gfxFT2Utils.h"
#include "gfxFT2FontList.h"
#include "gfxTextRun.h"
#include <locale.h>
#include "nsGkAtoms.h"
#include "nsTArray.h"
#include "nsUnicodeRange.h"
#include "nsCRT.h"
#include "nsXULAppAPI.h"
#include "mozilla/Logging.h"
#include "prinit.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Preferences.h"
#include "mozilla/gfx/2D.h"
/**
* gfxFT2Font
*/
bool
gfxFT2Font::ShapeText(DrawTarget *aDrawTarget,
const char16_t *aText,
uint32_t aOffset,
uint32_t aLength,
Script aScript,
bool aVertical,
gfxShapedText *aShapedText)
{
if (!gfxFont::ShapeText(aDrawTarget, aText, aOffset, aLength, aScript,
aVertical, aShapedText)) {
// harfbuzz must have failed(?!), just render raw glyphs
AddRange(aText, aOffset, aLength, aShapedText);
PostShapingFixup(aDrawTarget, aText, aOffset, aLength,
aVertical, aShapedText);
}
return true;
}
void
gfxFT2Font::AddRange(const char16_t *aText, uint32_t aOffset,
uint32_t aLength, gfxShapedText *aShapedText)
{
const uint32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit();
// we'll pass this in/figure it out dynamically, but at this point there can be only one face.
gfxFT2LockedFace faceLock(this);
FT_Face face = faceLock.get();
gfxShapedText::CompressedGlyph *charGlyphs =
aShapedText->GetCharacterGlyphs();
const gfxFT2Font::CachedGlyphData *cgd = nullptr, *cgdNext = nullptr;
FT_UInt spaceGlyph = GetSpaceGlyph();
for (uint32_t i = 0; i < aLength; i++, aOffset++) {
char16_t ch = aText[i];
if (ch == 0) {
// treat this null byte as a missing glyph, don't create a glyph for it
aShapedText->SetMissingGlyph(aOffset, 0, this);
continue;
}
NS_ASSERTION(!gfxFontGroup::IsInvalidChar(ch), "Invalid char detected");
if (cgdNext) {
cgd = cgdNext;
cgdNext = nullptr;
} else {
cgd = GetGlyphDataForChar(ch);
}
FT_UInt gid = cgd->glyphIndex;
int32_t advance = 0;
if (gid == 0) {
advance = -1; // trigger the missing glyphs case below
} else {
// find next character and its glyph -- in case they exist
// and exist in the current font face -- to compute kerning
char16_t chNext = 0;
FT_UInt gidNext = 0;
FT_Pos lsbDeltaNext = 0;
if (FT_HAS_KERNING(face) && i + 1 < aLength) {
chNext = aText[i + 1];
if (chNext != 0) {
cgdNext = GetGlyphDataForChar(chNext);
gidNext = cgdNext->glyphIndex;
if (gidNext && gidNext != spaceGlyph)
lsbDeltaNext = cgdNext->lsbDelta;
}
}
advance = cgd->xAdvance;
// now add kerning to the current glyph's advance
if (chNext && gidNext) {
FT_Vector kerning; kerning.x = 0;
FT_Get_Kerning(face, gid, gidNext, FT_KERNING_DEFAULT, &kerning);
advance += kerning.x;
if (cgd->rsbDelta - lsbDeltaNext >= 32) {
advance -= 64;
} else if (cgd->rsbDelta - lsbDeltaNext < -32) {
advance += 64;
}
}
// convert 26.6 fixed point to app units
// round rather than truncate to nearest pixel
// because these advances are often scaled
advance = ((advance * appUnitsPerDevUnit + 32) >> 6);
}
if (advance >= 0 &&
gfxShapedText::CompressedGlyph::IsSimpleAdvance(advance) &&
gfxShapedText::CompressedGlyph::IsSimpleGlyphID(gid)) {
charGlyphs[aOffset].SetSimpleGlyph(advance, gid);
} else if (gid == 0) {
// gid = 0 only happens when the glyph is missing from the font
aShapedText->SetMissingGlyph(aOffset, ch, this);
} else {
gfxTextRun::DetailedGlyph details;
details.mGlyphID = gid;
NS_ASSERTION(details.mGlyphID == gid,
"Seriously weird glyph ID detected!");
details.mAdvance = advance;
details.mXOffset = 0;
details.mYOffset = 0;
gfxShapedText::CompressedGlyph g;
g.SetComplex(charGlyphs[aOffset].IsClusterStart(), true, 1);
aShapedText->SetGlyphs(aOffset, g, &details);
}
}
}
gfxFT2Font::gfxFT2Font(cairo_scaled_font_t *aCairoFont,
FT2FontEntry *aFontEntry,
const gfxFontStyle *aFontStyle,
bool aNeedsBold)
: gfxFT2FontBase(aCairoFont, aFontEntry, aFontStyle)
, mCharGlyphCache(32)
{
NS_ASSERTION(mFontEntry, "Unable to find font entry for font. Something is whack.");
mApplySyntheticBold = aNeedsBold;
}
gfxFT2Font::~gfxFT2Font()
{
}
void
gfxFT2Font::FillGlyphDataForChar(uint32_t ch, CachedGlyphData *gd)
{
gfxFT2LockedFace faceLock(this);
FT_Face face = faceLock.get();
if (!face->charmap || face->charmap->encoding != FT_ENCODING_UNICODE) {
FT_Select_Charmap(face, FT_ENCODING_UNICODE);
}
FT_UInt gid = FT_Get_Char_Index(face, ch);
if (gid == 0) {
// this font doesn't support this char!
NS_ASSERTION(gid != 0, "We don't have a glyph, but font indicated that it supported this char in tables?");
gd->glyphIndex = 0;
return;
}
FT_Int32 flags = gfxPlatform::GetPlatform()->FontHintingEnabled() ?
FT_LOAD_DEFAULT :
(FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING);
FT_Error err = FT_Load_Glyph(face, gid, flags);
if (err) {
// hmm, this is weird, we failed to load a glyph that we had?
NS_WARNING("Failed to load glyph that we got from Get_Char_index");
gd->glyphIndex = 0;
return;
}
gd->glyphIndex = gid;
gd->lsbDelta = face->glyph->lsb_delta;
gd->rsbDelta = face->glyph->rsb_delta;
gd->xAdvance = face->glyph->advance.x;
}
void
gfxFT2Font::AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontCacheSizes* aSizes) const
{
gfxFont::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
aSizes->mFontInstances +=
mCharGlyphCache.ShallowSizeOfExcludingThis(aMallocSizeOf);
}
void
gfxFT2Font::AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontCacheSizes* aSizes) const
{
aSizes->mFontInstances += aMallocSizeOf(this);
AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
}

84
gfx/thebes/gfxFT2Fonts.h Normal file
View file

@ -0,0 +1,84 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_FT2FONTS_H
#define GFX_FT2FONTS_H
#include "mozilla/MemoryReporting.h"
#include "cairo.h"
#include "gfxTypes.h"
#include "gfxFont.h"
#include "gfxFT2FontBase.h"
#include "gfxContext.h"
#include "gfxFontUtils.h"
#include "gfxUserFontSet.h"
class FT2FontEntry;
class gfxFT2Font : public gfxFT2FontBase {
public: // new functions
gfxFT2Font(cairo_scaled_font_t *aCairoFont,
FT2FontEntry *aFontEntry,
const gfxFontStyle *aFontStyle,
bool aNeedsBold);
virtual ~gfxFT2Font ();
FT2FontEntry *GetFontEntry();
struct CachedGlyphData {
CachedGlyphData()
: glyphIndex(0xffffffffU) { }
CachedGlyphData(uint32_t gid)
: glyphIndex(gid) { }
uint32_t glyphIndex;
int32_t lsbDelta;
int32_t rsbDelta;
int32_t xAdvance;
};
const CachedGlyphData* GetGlyphDataForChar(uint32_t ch) {
CharGlyphMapEntryType *entry = mCharGlyphCache.PutEntry(ch);
if (!entry)
return nullptr;
if (entry->mData.glyphIndex == 0xffffffffU) {
// this is a new entry, fill it
FillGlyphDataForChar(ch, &entry->mData);
}
return &entry->mData;
}
virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontCacheSizes* aSizes) const override;
virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontCacheSizes* aSizes) const override;
protected:
virtual bool ShapeText(DrawTarget *aDrawTarget,
const char16_t *aText,
uint32_t aOffset,
uint32_t aLength,
Script aScript,
bool aVertical,
gfxShapedText *aShapedText) override;
void FillGlyphDataForChar(uint32_t ch, CachedGlyphData *gd);
void AddRange(const char16_t *aText,
uint32_t aOffset,
uint32_t aLength,
gfxShapedText *aShapedText);
typedef nsBaseHashtableET<nsUint32HashKey, CachedGlyphData> CharGlyphMapEntryType;
typedef nsTHashtable<CharGlyphMapEntryType> CharGlyphMap;
CharGlyphMap mCharGlyphCache;
};
#endif /* GFX_FT2FONTS_H */

378
gfx/thebes/gfxFT2Utils.cpp Normal file
View file

@ -0,0 +1,378 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gfxFT2FontBase.h"
#include "gfxFT2Utils.h"
#include "mozilla/Likely.h"
#include FT_TRUETYPE_TAGS_H
#include FT_TRUETYPE_TABLES_H
#include <algorithm>
#ifdef HAVE_FONTCONFIG_FCFREETYPE_H
#include <fontconfig/fcfreetype.h>
#endif
#include "prlink.h"
// aScale is intended for a 16.16 x/y_scale of an FT_Size_Metrics
static inline FT_Long
ScaleRoundDesignUnits(FT_Short aDesignMetric, FT_Fixed aScale)
{
FT_Long fixed26dot6 = FT_MulFix(aDesignMetric, aScale);
return ROUND_26_6_TO_INT(fixed26dot6);
}
// Snap a line to pixels while keeping the center and size of the line as
// close to the original position as possible.
//
// Pango does similar snapping for underline and strikethrough when fonts are
// hinted, but nsCSSRendering::GetTextDecorationRectInternal always snaps the
// top and size of lines. Optimizing the distance between the line and
// baseline is probably good for the gap between text and underline, but
// optimizing the center of the line is better for positioning strikethough.
static void
SnapLineToPixels(gfxFloat& aOffset, gfxFloat& aSize)
{
gfxFloat snappedSize = std::max(floor(aSize + 0.5), 1.0);
// Correct offset for change in size
gfxFloat offset = aOffset - 0.5 * (aSize - snappedSize);
// Snap offset
aOffset = floor(offset + 0.5);
aSize = snappedSize;
}
void
gfxFT2LockedFace::GetMetrics(gfxFont::Metrics* aMetrics,
uint32_t* aSpaceGlyph)
{
NS_PRECONDITION(aMetrics != nullptr, "aMetrics must not be NULL");
NS_PRECONDITION(aSpaceGlyph != nullptr, "aSpaceGlyph must not be NULL");
if (MOZ_UNLIKELY(!mFace)) {
// No face. This unfortunate situation might happen if the font
// file is (re)moved at the wrong time.
const gfxFloat emHeight = mGfxFont->GetStyle()->size;
aMetrics->emHeight = emHeight;
aMetrics->maxAscent = aMetrics->emAscent = 0.8 * emHeight;
aMetrics->maxDescent = aMetrics->emDescent = 0.2 * emHeight;
aMetrics->maxHeight = emHeight;
aMetrics->internalLeading = 0.0;
aMetrics->externalLeading = 0.2 * emHeight;
const gfxFloat spaceWidth = 0.5 * emHeight;
aMetrics->spaceWidth = spaceWidth;
aMetrics->maxAdvance = spaceWidth;
aMetrics->aveCharWidth = spaceWidth;
aMetrics->zeroOrAveCharWidth = spaceWidth;
const gfxFloat xHeight = 0.5 * emHeight;
aMetrics->xHeight = xHeight;
aMetrics->capHeight = aMetrics->maxAscent;
const gfxFloat underlineSize = emHeight / 14.0;
aMetrics->underlineSize = underlineSize;
aMetrics->underlineOffset = -underlineSize;
aMetrics->strikeoutOffset = 0.25 * emHeight;
aMetrics->strikeoutSize = underlineSize;
*aSpaceGlyph = 0;
return;
}
const FT_Size_Metrics& ftMetrics = mFace->size->metrics;
gfxFloat emHeight;
// Scale for vertical design metric conversion: pixels per design unit.
// If this remains at 0.0, we can't use metrics from OS/2 etc.
gfxFloat yScale = 0.0;
if (FT_IS_SCALABLE(mFace)) {
// Prefer FT_Size_Metrics::x_scale to x_ppem as x_ppem does not
// have subpixel accuracy.
//
// FT_Size_Metrics::y_scale is in 16.16 fixed point format. Its
// (fractional) value is a factor that converts vertical metrics from
// design units to units of 1/64 pixels, so that the result may be
// interpreted as pixels in 26.6 fixed point format.
yScale = FLOAT_FROM_26_6(FLOAT_FROM_16_16(ftMetrics.y_scale));
emHeight = mFace->units_per_EM * yScale;
} else { // Not scalable.
emHeight = ftMetrics.y_ppem;
// FT_Face doc says units_per_EM and a bunch of following fields
// are "only relevant to scalable outlines". If it's an sfnt,
// we can get units_per_EM from the 'head' table instead; otherwise,
// we don't have a unitsPerEm value so we can't compute/use yScale.
const TT_Header* head =
static_cast<TT_Header*>(FT_Get_Sfnt_Table(mFace, ft_sfnt_head));
if (head) {
gfxFloat emUnit = head->Units_Per_EM;
yScale = emHeight / emUnit;
}
}
TT_OS2 *os2 =
static_cast<TT_OS2*>(FT_Get_Sfnt_Table(mFace, ft_sfnt_os2));
aMetrics->maxAscent = FLOAT_FROM_26_6(ftMetrics.ascender);
aMetrics->maxDescent = -FLOAT_FROM_26_6(ftMetrics.descender);
aMetrics->maxAdvance = FLOAT_FROM_26_6(ftMetrics.max_advance);
gfxFloat lineHeight;
if (os2 && os2->sTypoAscender && yScale > 0.0) {
aMetrics->emAscent = os2->sTypoAscender * yScale;
aMetrics->emDescent = -os2->sTypoDescender * yScale;
FT_Short typoHeight =
os2->sTypoAscender - os2->sTypoDescender + os2->sTypoLineGap;
lineHeight = typoHeight * yScale;
// If the OS/2 fsSelection USE_TYPO_METRICS bit is set,
// set maxAscent/Descent from the sTypo* fields instead of hhea.
const uint16_t kUseTypoMetricsMask = 1 << 7;
if (os2->fsSelection & kUseTypoMetricsMask) {
aMetrics->maxAscent = NS_round(aMetrics->emAscent);
aMetrics->maxDescent = NS_round(aMetrics->emDescent);
} else {
// maxAscent/maxDescent get used for frame heights, and some fonts
// don't have the HHEA table ascent/descent set (bug 279032).
// We use NS_round here to parallel the pixel-rounded values that
// freetype gives us for ftMetrics.ascender/descender.
aMetrics->maxAscent =
std::max(aMetrics->maxAscent, NS_round(aMetrics->emAscent));
aMetrics->maxDescent =
std::max(aMetrics->maxDescent, NS_round(aMetrics->emDescent));
}
} else {
aMetrics->emAscent = aMetrics->maxAscent;
aMetrics->emDescent = aMetrics->maxDescent;
lineHeight = FLOAT_FROM_26_6(ftMetrics.height);
}
cairo_text_extents_t extents;
*aSpaceGlyph = GetCharExtents(' ', &extents);
if (*aSpaceGlyph) {
aMetrics->spaceWidth = extents.x_advance;
} else {
aMetrics->spaceWidth = aMetrics->maxAdvance; // guess
}
aMetrics->zeroOrAveCharWidth = 0.0;
if (GetCharExtents('0', &extents)) {
aMetrics->zeroOrAveCharWidth = extents.x_advance;
}
// Prefering a measured x over sxHeight because sxHeight doesn't consider
// hinting, but maybe the x extents are not quite right in some fancy
// script fonts. CSS 2.1 suggests possibly using the height of an "o",
// which would have a more consistent glyph across fonts.
if (GetCharExtents('x', &extents) && extents.y_bearing < 0.0) {
aMetrics->xHeight = -extents.y_bearing;
aMetrics->aveCharWidth = extents.x_advance;
} else {
if (os2 && os2->sxHeight && yScale > 0.0) {
aMetrics->xHeight = os2->sxHeight * yScale;
} else {
// CSS 2.1, section 4.3.2 Lengths: "In the cases where it is
// impossible or impractical to determine the x-height, a value of
// 0.5em should be used."
aMetrics->xHeight = 0.5 * emHeight;
}
aMetrics->aveCharWidth = 0.0; // updated below
}
if (GetCharExtents('H', &extents) && extents.y_bearing < 0.0) {
aMetrics->capHeight = -extents.y_bearing;
} else {
if (os2 && os2->sCapHeight && yScale > 0.0) {
aMetrics->capHeight = os2->sCapHeight * yScale;
} else {
aMetrics->capHeight = aMetrics->maxAscent;
}
}
// aveCharWidth is used for the width of text input elements so be
// liberal rather than conservative in the estimate.
if (os2 && os2->xAvgCharWidth) {
// Round to pixels as this is compared with maxAdvance to guess
// whether this is a fixed width font.
gfxFloat avgCharWidth =
ScaleRoundDesignUnits(os2->xAvgCharWidth, ftMetrics.x_scale);
aMetrics->aveCharWidth =
std::max(aMetrics->aveCharWidth, avgCharWidth);
}
aMetrics->aveCharWidth =
std::max(aMetrics->aveCharWidth, aMetrics->zeroOrAveCharWidth);
if (aMetrics->aveCharWidth == 0.0) {
aMetrics->aveCharWidth = aMetrics->spaceWidth;
}
if (aMetrics->zeroOrAveCharWidth == 0.0) {
aMetrics->zeroOrAveCharWidth = aMetrics->aveCharWidth;
}
// Apparently hinting can mean that max_advance is not always accurate.
aMetrics->maxAdvance =
std::max(aMetrics->maxAdvance, aMetrics->aveCharWidth);
// gfxFont::Metrics::underlineOffset is the position of the top of the
// underline.
//
// FT_FaceRec documentation describes underline_position as "the
// center of the underlining stem". This was the original definition
// of the PostScript metric, but in the PostScript table of OpenType
// fonts the metric is "the top of the underline"
// (http://www.microsoft.com/typography/otspec/post.htm), and FreeType
// (up to version 2.3.7) doesn't make any adjustment.
//
// Therefore get the underline position directly from the table
// ourselves when this table exists. Use FreeType's metrics for
// other (including older PostScript) fonts.
if (mFace->underline_position && mFace->underline_thickness && yScale > 0.0) {
aMetrics->underlineSize = mFace->underline_thickness * yScale;
TT_Postscript *post = static_cast<TT_Postscript*>
(FT_Get_Sfnt_Table(mFace, ft_sfnt_post));
if (post && post->underlinePosition) {
aMetrics->underlineOffset = post->underlinePosition * yScale;
} else {
aMetrics->underlineOffset = mFace->underline_position * yScale
+ 0.5 * aMetrics->underlineSize;
}
} else { // No underline info.
// Imitate Pango.
aMetrics->underlineSize = emHeight / 14.0;
aMetrics->underlineOffset = -aMetrics->underlineSize;
}
if (os2 && os2->yStrikeoutSize && os2->yStrikeoutPosition && yScale > 0.0) {
aMetrics->strikeoutSize = os2->yStrikeoutSize * yScale;
aMetrics->strikeoutOffset = os2->yStrikeoutPosition * yScale;
} else { // No strikeout info.
aMetrics->strikeoutSize = aMetrics->underlineSize;
// Use OpenType spec's suggested position for Roman font.
aMetrics->strikeoutOffset = emHeight * 409.0 / 2048.0
+ 0.5 * aMetrics->strikeoutSize;
}
SnapLineToPixels(aMetrics->strikeoutOffset, aMetrics->strikeoutSize);
aMetrics->maxHeight = aMetrics->maxAscent + aMetrics->maxDescent;
// Make the line height an integer number of pixels so that lines will be
// equally spaced (rather than just being snapped to pixels, some up and
// some down). Layout calculates line height from the emHeight +
// internalLeading + externalLeading, but first each of these is rounded
// to layout units. To ensure that the result is an integer number of
// pixels, round each of the components to pixels.
aMetrics->emHeight = floor(emHeight + 0.5);
// maxHeight will normally be an integer, but round anyway in case
// FreeType is configured differently.
aMetrics->internalLeading =
floor(aMetrics->maxHeight - aMetrics->emHeight + 0.5);
// Text input boxes currently don't work well with lineHeight
// significantly less than maxHeight (with Verdana, for example).
lineHeight = floor(std::max(lineHeight, aMetrics->maxHeight) + 0.5);
aMetrics->externalLeading =
lineHeight - aMetrics->internalLeading - aMetrics->emHeight;
// Ensure emAscent + emDescent == emHeight
gfxFloat sum = aMetrics->emAscent + aMetrics->emDescent;
aMetrics->emAscent = sum > 0.0 ?
aMetrics->emAscent * aMetrics->emHeight / sum : 0.0;
aMetrics->emDescent = aMetrics->emHeight - aMetrics->emAscent;
}
uint32_t
gfxFT2LockedFace::GetGlyph(uint32_t aCharCode)
{
if (MOZ_UNLIKELY(!mFace))
return 0;
#ifdef HAVE_FONTCONFIG_FCFREETYPE_H
// FcFreeTypeCharIndex will search starting from the most recently
// selected charmap. This can cause non-determistic behavior when more
// than one charmap supports a character but with different glyphs, as
// with older versions of MS Gothic, for example. Always prefer a Unicode
// charmap, if there is one. (FcFreeTypeCharIndex usually does the
// appropriate Unicode conversion, but some fonts have non-Roman glyphs
// for FT_ENCODING_APPLE_ROMAN characters.)
if (!mFace->charmap || mFace->charmap->encoding != FT_ENCODING_UNICODE) {
FT_Select_Charmap(mFace, FT_ENCODING_UNICODE);
}
return FcFreeTypeCharIndex(mFace, aCharCode);
#else
return FT_Get_Char_Index(mFace, aCharCode);
#endif
}
typedef FT_UInt (*GetCharVariantFunction)(FT_Face face,
FT_ULong charcode,
FT_ULong variantSelector);
uint32_t
gfxFT2LockedFace::GetUVSGlyph(uint32_t aCharCode, uint32_t aVariantSelector)
{
NS_PRECONDITION(aVariantSelector, "aVariantSelector should not be NULL");
if (MOZ_UNLIKELY(!mFace))
return 0;
// This function is available from FreeType 2.3.6 (June 2008).
static CharVariantFunction sGetCharVariantPtr = FindCharVariantFunction();
if (!sGetCharVariantPtr)
return 0;
#ifdef HAVE_FONTCONFIG_FCFREETYPE_H
// FcFreeTypeCharIndex may have changed the selected charmap.
// FT_Face_GetCharVariantIndex needs a unicode charmap.
if (!mFace->charmap || mFace->charmap->encoding != FT_ENCODING_UNICODE) {
FT_Select_Charmap(mFace, FT_ENCODING_UNICODE);
}
#endif
return (*sGetCharVariantPtr)(mFace, aCharCode, aVariantSelector);
}
uint32_t
gfxFT2LockedFace::GetCharExtents(char aChar, cairo_text_extents_t* aExtents)
{
NS_PRECONDITION(aExtents != nullptr, "aExtents must not be NULL");
if (!mFace)
return 0;
FT_UInt gid = mGfxFont->GetGlyph(aChar);
if (gid) {
mGfxFont->GetGlyphExtents(gid, aExtents);
}
return gid;
}
gfxFT2LockedFace::CharVariantFunction
gfxFT2LockedFace::FindCharVariantFunction()
{
// This function is available from FreeType 2.3.6 (June 2008).
PRLibrary *lib = nullptr;
CharVariantFunction function =
reinterpret_cast<CharVariantFunction>
(PR_FindFunctionSymbolAndLibrary("FT_Face_GetCharVariantIndex", &lib));
if (!lib) {
return nullptr;
}
FT_Int major;
FT_Int minor;
FT_Int patch;
FT_Library_Version(mFace->glyph->library, &major, &minor, &patch);
// Versions 2.4.0 to 2.4.3 crash if configured with
// FT_CONFIG_OPTION_OLD_INTERNALS. Presence of the symbol FT_Alloc
// indicates FT_CONFIG_OPTION_OLD_INTERNALS.
if (major == 2 && minor == 4 && patch < 4 &&
PR_FindFunctionSymbol(lib, "FT_Alloc")) {
function = nullptr;
}
// Decrement the reference count incremented in
// PR_FindFunctionSymbolAndLibrary.
PR_UnloadLibrary(lib);
return function;
}

94
gfx/thebes/gfxFT2Utils.h Normal file
View file

@ -0,0 +1,94 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_FT2UTILS_H
#define GFX_FT2UTILS_H
#include "cairo-ft.h"
#include "gfxFT2FontBase.h"
#include "mozilla/Likely.h"
// Rounding and truncation functions for a FreeType fixed point number
// (FT26Dot6) stored in a 32bit integer with high 26 bits for the integer
// part and low 6 bits for the fractional part.
#define FLOAT_FROM_26_6(x) ((x) / 64.0)
#define FLOAT_FROM_16_16(x) ((x) / 65536.0)
#define ROUND_26_6_TO_INT(x) ((x) >= 0 ? ((32 + (x)) >> 6) \
: -((32 - (x)) >> 6))
typedef struct FT_FaceRec_* FT_Face;
class gfxFT2LockedFace {
public:
explicit gfxFT2LockedFace(gfxFT2FontBase *aFont) :
mGfxFont(aFont),
mFace(cairo_ft_scaled_font_lock_face(aFont->CairoScaledFont()))
{ }
~gfxFT2LockedFace()
{
if (mFace) {
cairo_ft_scaled_font_unlock_face(mGfxFont->CairoScaledFont());
}
}
FT_Face get() { return mFace; };
/**
* Get the glyph id for a Unicode character representable by a single
* glyph, or return zero if there is no such glyph. This does no caching,
* so you probably want gfxFcFont::GetGlyph.
*/
uint32_t GetGlyph(uint32_t aCharCode);
/**
* Returns 0 if there is no variation selector cmap subtable.
*/
uint32_t GetUVSGlyph(uint32_t aCharCode, uint32_t aVariantSelector);
void GetMetrics(gfxFont::Metrics* aMetrics, uint32_t* aSpaceGlyph);
// A scale factor for use in converting horizontal metrics from font units
// to pixels.
gfxFloat XScale()
{
if (MOZ_UNLIKELY(!mFace))
return 0.0;
const FT_Size_Metrics& ftMetrics = mFace->size->metrics;
if (FT_IS_SCALABLE(mFace)) {
// Prefer FT_Size_Metrics::x_scale to x_ppem as x_ppem does not
// have subpixel accuracy.
//
// FT_Size_Metrics::x_scale is in 16.16 fixed point format. Its
// (fractional) value is a factor that converts vertical metrics
// from design units to units of 1/64 pixels, so that the result
// may be interpreted as pixels in 26.6 fixed point format.
return FLOAT_FROM_26_6(FLOAT_FROM_16_16(ftMetrics.x_scale));
}
// Not scalable.
// FT_Size_Metrics doc says x_scale is "only relevant for scalable
// font formats".
return gfxFloat(ftMetrics.x_ppem) / gfxFloat(mFace->units_per_EM);
}
protected:
/**
* Get extents for a simple character representable by a single glyph.
* The return value is the glyph id of that glyph or zero if no such glyph
* exists. aExtents is only set when this returns a non-zero glyph id.
*/
uint32_t GetCharExtents(char aChar, cairo_text_extents_t* aExtents);
typedef FT_UInt (*CharVariantFunction)(FT_Face face,
FT_ULong charcode,
FT_ULong variantSelector);
CharVariantFunction FindCharVariantFunction();
RefPtr<gfxFT2FontBase> mGfxFont;
FT_Face mFace;
};
#endif /* GFX_FT2UTILS_H */

25
gfx/thebes/gfxFailure.h Normal file
View file

@ -0,0 +1,25 @@
/* vim: se cin sw=2 ts=2 et : */
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef gfxFailure_h_
#define gfxFailure_h_
#include "nsString.h"
#include "nsIGfxInfo.h"
#include "nsServiceManagerUtils.h"
namespace mozilla {
namespace gfx {
inline
void LogFailure(const nsCString &failure) {
nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
gfxInfo->LogFailure(failure);
}
} // namespace gfx
} // namespace mozilla
#endif // gfxFailure_h_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,330 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFXFCPLATFORMFONTLIST_H_
#define GFXFCPLATFORMFONTLIST_H_
#include "gfxFont.h"
#include "gfxFontEntry.h"
#include "gfxFT2FontBase.h"
#include "gfxPlatformFontList.h"
#include "mozilla/mozalloc.h"
#include "nsClassHashtable.h"
#include <fontconfig/fontconfig.h>
#include "ft2build.h"
#include FT_FREETYPE_H
#include FT_TRUETYPE_TABLES_H
#include <cairo.h>
#include <cairo-ft.h>
#include "gfxFontconfigUtils.h" // xxx - only for nsAutoRefTraits<FcPattern>, etc.
template <>
class nsAutoRefTraits<FcObjectSet> : public nsPointerRefTraits<FcObjectSet>
{
public:
static void Release(FcObjectSet *ptr) { FcObjectSetDestroy(ptr); }
};
template <>
class nsAutoRefTraits<FcConfig> : public nsPointerRefTraits<FcConfig>
{
public:
static void Release(FcConfig *ptr) { FcConfigDestroy(ptr); }
static void AddRef(FcConfig *ptr) { FcConfigReference(ptr); }
};
// Helper classes used for clearning out user font data when cairo font
// face is destroyed. Since multiple faces may use the same data, be
// careful to assure that the data is only cleared out when all uses
// expire. The font entry object contains a refptr to FTUserFontData and
// each cairo font created from that font entry contains a
// FTUserFontDataRef with a refptr to that same FTUserFontData object.
class FTUserFontData {
public:
NS_INLINE_DECL_REFCOUNTING(FTUserFontData)
explicit FTUserFontData(FT_Face aFace, const uint8_t* aData)
: mFace(aFace), mFontData(aData)
{
}
const uint8_t *FontData() const { return mFontData; }
private:
~FTUserFontData()
{
FT_Done_Face(mFace);
if (mFontData) {
NS_Free((void*)mFontData);
}
}
FT_Face mFace;
const uint8_t *mFontData;
};
class FTUserFontDataRef {
public:
explicit FTUserFontDataRef(FTUserFontData *aUserFontData)
: mUserFontData(aUserFontData)
{
}
static void Destroy(void* aData) {
FTUserFontDataRef* aUserFontDataRef =
static_cast<FTUserFontDataRef*>(aData);
delete aUserFontDataRef;
}
private:
RefPtr<FTUserFontData> mUserFontData;
};
// The names for the font entry and font classes should really
// the common 'Fc' abbreviation but the gfxPangoFontGroup code already
// defines versions of these, so use the verbose name for now.
class gfxFontconfigFontEntry : public gfxFontEntry {
public:
// used for system fonts with explicit patterns
explicit gfxFontconfigFontEntry(const nsAString& aFaceName,
FcPattern* aFontPattern,
bool aIgnoreFcCharmap);
// used for data fonts where the fontentry takes ownership
// of the font data and the FT_Face
explicit gfxFontconfigFontEntry(const nsAString& aFaceName,
uint16_t aWeight,
int16_t aStretch,
uint8_t aStyle,
const uint8_t *aData,
FT_Face aFace);
// used for @font-face local system fonts with explicit patterns
explicit gfxFontconfigFontEntry(const nsAString& aFaceName,
FcPattern* aFontPattern,
uint16_t aWeight,
int16_t aStretch,
uint8_t aStyle);
FcPattern* GetPattern() { return mFontPattern; }
bool SupportsLangGroup(nsIAtom *aLangGroup) const override;
nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr) override;
bool TestCharacterMap(uint32_t aCh) override;
hb_blob_t* GetFontTable(uint32_t aTableTag) override;
void ForgetHBFace() override;
void ReleaseGrFace(gr_face* aFace) override;
double GetAspect();
protected:
virtual ~gfxFontconfigFontEntry();
gfxFont *CreateFontInstance(const gfxFontStyle *aFontStyle,
bool aNeedsBold) override;
// helper method for creating cairo font from pattern
cairo_scaled_font_t*
CreateScaledFont(FcPattern* aRenderPattern,
gfxFloat aAdjustedSize,
const gfxFontStyle *aStyle,
bool aNeedsBold);
// override to pull data from FTFace
virtual nsresult
CopyFontTable(uint32_t aTableTag,
nsTArray<uint8_t>& aBuffer) override;
// if HB or GR faces are gone, close down the FT_Face
void MaybeReleaseFTFace();
// pattern for a single face of a family
nsCountedRef<FcPattern> mFontPattern;
// user font data, when needed
RefPtr<FTUserFontData> mUserFontData;
// FTFace - initialized when needed
FT_Face mFTFace;
bool mFTFaceInitialized;
// Whether TestCharacterMap should check the actual cmap rather than asking
// fontconfig about character coverage.
// We do this for app-bundled (rather than system) fonts, as they may
// include color glyphs that fontconfig would overlook, and for fonts
// loaded via @font-face.
bool mIgnoreFcCharmap;
double mAspect;
// data font
const uint8_t* mFontData;
};
class gfxFontconfigFontFamily : public gfxFontFamily {
public:
explicit gfxFontconfigFontFamily(const nsAString& aName) :
gfxFontFamily(aName),
mContainsAppFonts(false),
mHasNonScalableFaces(false)
{ }
void FindStyleVariations(FontInfoData *aFontInfoData = nullptr) override;
// Families are constructed initially with just references to patterns.
// When necessary, these are enumerated within FindStyleVariations.
void AddFontPattern(FcPattern* aFontPattern);
void SetFamilyContainsAppFonts(bool aContainsAppFonts)
{
mContainsAppFonts = aContainsAppFonts;
}
void
FindAllFontsForStyle(const gfxFontStyle& aFontStyle,
nsTArray<gfxFontEntry*>& aFontEntryList,
bool& aNeedsSyntheticBold) override;
protected:
virtual ~gfxFontconfigFontFamily() { }
nsTArray<nsCountedRef<FcPattern> > mFontPatterns;
bool mContainsAppFonts;
bool mHasNonScalableFaces;
};
class gfxFontconfigFont : public gfxFontconfigFontBase {
public:
gfxFontconfigFont(cairo_scaled_font_t *aScaledFont,
FcPattern *aPattern,
gfxFloat aAdjustedSize,
gfxFontEntry *aFontEntry,
const gfxFontStyle *aFontStyle,
bool aNeedsBold);
protected:
virtual ~gfxFontconfigFont();
};
class nsILanguageAtomService;
class gfxFcPlatformFontList : public gfxPlatformFontList {
public:
gfxFcPlatformFontList();
static gfxFcPlatformFontList* PlatformFontList() {
return static_cast<gfxFcPlatformFontList*>(sPlatformFontList);
}
// initialize font lists
virtual nsresult InitFontListForPlatform() override;
void GetFontList(nsIAtom *aLangGroup,
const nsACString& aGenericFamily,
nsTArray<nsString>& aListOfFonts) override;
gfxFontEntry*
LookupLocalFont(const nsAString& aFontName, uint16_t aWeight,
int16_t aStretch, uint8_t aStyle) override;
gfxFontEntry*
MakePlatformFont(const nsAString& aFontName, uint16_t aWeight,
int16_t aStretch,
uint8_t aStyle,
const uint8_t* aFontData,
uint32_t aLength) override;
bool FindAndAddFamilies(const nsAString& aFamily,
nsTArray<gfxFontFamily*>* aOutput,
gfxFontStyle* aStyle = nullptr,
gfxFloat aDevToCssSize = 1.0) override;
bool GetStandardFamilyName(const nsAString& aFontName,
nsAString& aFamilyName) override;
FcConfig* GetLastConfig() const { return mLastConfig; }
// override to use fontconfig lookup for generics
void AddGenericFonts(mozilla::FontFamilyType aGenericType,
nsIAtom* aLanguage,
nsTArray<gfxFontFamily*>& aFamilyList) override;
void ClearLangGroupPrefFonts() override;
// clear out cached generic-lang ==> family-list mappings
void ClearGenericMappings() {
mGenericMappings.Clear();
}
static FT_Library GetFTLibrary();
protected:
virtual ~gfxFcPlatformFontList();
// Add all the font families found in a font set.
// aAppFonts indicates whether this is the system or application fontset.
void AddFontSetFamilies(FcFontSet* aFontSet, bool aAppFonts);
// figure out which families fontconfig maps a generic to
// (aGeneric assumed already lowercase)
PrefFontList* FindGenericFamilies(const nsAString& aGeneric,
nsIAtom* aLanguage);
// are all pref font settings set to use fontconfig generics?
bool PrefFontListsUseOnlyGenerics();
static void CheckFontUpdates(nsITimer *aTimer, void *aThis);
virtual gfxFontFamily*
GetDefaultFontForPlatform(const gfxFontStyle* aStyle) override;
#ifdef MOZ_BUNDLED_FONTS
void ActivateBundledFonts();
nsCString mBundledFontsPath;
bool mBundledFontsInitialized;
#endif
// to avoid enumerating all fonts, maintain a mapping of local font
// names to family
nsBaseHashtable<nsStringHashKey,
nsCountedRef<FcPattern>,
FcPattern*> mLocalNames;
// caching generic/lang ==> font family list
nsClassHashtable<nsCStringHashKey,
PrefFontList> mGenericMappings;
// Caching family lookups as found by FindAndAddFamilies after resolving
// substitutions. The gfxFontFamily objects cached here are owned by the
// gfxFcPlatformFontList via its mFamilies table; note that if the main
// font list is rebuilt (e.g. due to a fontconfig configuration change),
// these pointers will be invalidated. InitFontList() flushes the cache
// in this case.
nsDataHashtable<nsCStringHashKey,
nsTArray<gfxFontFamily*>> mFcSubstituteCache;
nsCOMPtr<nsITimer> mCheckFontUpdatesTimer;
nsCountedRef<FcConfig> mLastConfig;
// By default, font prefs under Linux are set to simply lookup
// via fontconfig the appropriate font for serif/sans-serif/monospace.
// Rather than check each time a font pref is used, check them all at startup
// and set a boolean to flag the case that non-default user font prefs exist
// Note: langGroup == x-math is handled separately
bool mAlwaysUseFontconfigGenerics;
static FT_Library sCairoFTLibrary;
};
#endif /* GFXPLATFORMFONTLIST_H_ */

4028
gfx/thebes/gfxFont.cpp Normal file

File diff suppressed because it is too large Load diff

2223
gfx/thebes/gfxFont.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,237 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* font constants shared by both thebes and layout */
#ifndef GFX_FONT_CONSTANTS_H
#define GFX_FONT_CONSTANTS_H
/*
* This file is separate from gfxFont.h so that layout can include it
* without bringing in gfxFont.h and everything it includes.
*/
#define NS_FONT_STYLE_NORMAL 0
#define NS_FONT_STYLE_ITALIC 1
#define NS_FONT_STYLE_OBLIQUE 2
#define NS_FONT_WEIGHT_NORMAL 400
#define NS_FONT_WEIGHT_BOLD 700
#define NS_FONT_WEIGHT_THIN 100
#define NS_FONT_STRETCH_ULTRA_CONDENSED (-4)
#define NS_FONT_STRETCH_EXTRA_CONDENSED (-3)
#define NS_FONT_STRETCH_CONDENSED (-2)
#define NS_FONT_STRETCH_SEMI_CONDENSED (-1)
#define NS_FONT_STRETCH_NORMAL 0
#define NS_FONT_STRETCH_SEMI_EXPANDED 1
#define NS_FONT_STRETCH_EXPANDED 2
#define NS_FONT_STRETCH_EXTRA_EXPANDED 3
#define NS_FONT_STRETCH_ULTRA_EXPANDED 4
#define NS_FONT_SMOOTHING_AUTO 0
#define NS_FONT_SMOOTHING_GRAYSCALE 1
#define NS_FONT_KERNING_AUTO 0
#define NS_FONT_KERNING_NONE 1
#define NS_FONT_KERNING_NORMAL 2
#define NS_FONT_SYNTHESIS_WEIGHT 0x1
#define NS_FONT_SYNTHESIS_STYLE 0x2
#define NS_FONT_DISPLAY_AUTO 0
#define NS_FONT_DISPLAY_BLOCK 1
#define NS_FONT_DISPLAY_SWAP 2
#define NS_FONT_DISPLAY_FALLBACK 3
#define NS_FONT_DISPLAY_OPTIONAL 4
enum {
eFeatureAlternates_historical,
eFeatureAlternates_stylistic,
eFeatureAlternates_styleset,
eFeatureAlternates_character_variant,
eFeatureAlternates_swash,
eFeatureAlternates_ornaments,
eFeatureAlternates_annotation,
eFeatureAlternates_numFeatures
};
// alternates - simple enumerated values
#define NS_FONT_VARIANT_ALTERNATES_HISTORICAL (1 << eFeatureAlternates_historical)
// alternates - values that use functional syntax
#define NS_FONT_VARIANT_ALTERNATES_STYLISTIC (1 << eFeatureAlternates_stylistic)
#define NS_FONT_VARIANT_ALTERNATES_STYLESET (1 << eFeatureAlternates_styleset)
#define NS_FONT_VARIANT_ALTERNATES_CHARACTER_VARIANT (1 << eFeatureAlternates_character_variant)
#define NS_FONT_VARIANT_ALTERNATES_SWASH (1 << eFeatureAlternates_swash)
#define NS_FONT_VARIANT_ALTERNATES_ORNAMENTS (1 << eFeatureAlternates_ornaments)
#define NS_FONT_VARIANT_ALTERNATES_ANNOTATION (1 << eFeatureAlternates_annotation)
#define NS_FONT_VARIANT_ALTERNATES_ENUMERATED_MASK \
NS_FONT_VARIANT_ALTERNATES_HISTORICAL
#define NS_FONT_VARIANT_ALTERNATES_FUNCTIONAL_MASK ( \
NS_FONT_VARIANT_ALTERNATES_STYLISTIC | \
NS_FONT_VARIANT_ALTERNATES_STYLESET | \
NS_FONT_VARIANT_ALTERNATES_CHARACTER_VARIANT | \
NS_FONT_VARIANT_ALTERNATES_SWASH | \
NS_FONT_VARIANT_ALTERNATES_ORNAMENTS | \
NS_FONT_VARIANT_ALTERNATES_ANNOTATION )
#define NS_FONT_VARIANT_CAPS_NORMAL 0
#define NS_FONT_VARIANT_CAPS_SMALLCAPS 1
#define NS_FONT_VARIANT_CAPS_ALLSMALL 2
#define NS_FONT_VARIANT_CAPS_PETITECAPS 3
#define NS_FONT_VARIANT_CAPS_ALLPETITE 4
#define NS_FONT_VARIANT_CAPS_TITLING 5
#define NS_FONT_VARIANT_CAPS_UNICASE 6
enum {
eFeatureEastAsian_jis78,
eFeatureEastAsian_jis83,
eFeatureEastAsian_jis90,
eFeatureEastAsian_jis04,
eFeatureEastAsian_simplified,
eFeatureEastAsian_traditional,
eFeatureEastAsian_full_width,
eFeatureEastAsian_prop_width,
eFeatureEastAsian_ruby,
eFeatureEastAsian_numFeatures
};
#define NS_FONT_VARIANT_EAST_ASIAN_JIS78 (1 << eFeatureEastAsian_jis78)
#define NS_FONT_VARIANT_EAST_ASIAN_JIS83 (1 << eFeatureEastAsian_jis83)
#define NS_FONT_VARIANT_EAST_ASIAN_JIS90 (1 << eFeatureEastAsian_jis90)
#define NS_FONT_VARIANT_EAST_ASIAN_JIS04 (1 << eFeatureEastAsian_jis04)
#define NS_FONT_VARIANT_EAST_ASIAN_SIMPLIFIED (1 << eFeatureEastAsian_simplified)
#define NS_FONT_VARIANT_EAST_ASIAN_TRADITIONAL (1 << eFeatureEastAsian_traditional)
#define NS_FONT_VARIANT_EAST_ASIAN_FULL_WIDTH (1 << eFeatureEastAsian_full_width)
#define NS_FONT_VARIANT_EAST_ASIAN_PROP_WIDTH (1 << eFeatureEastAsian_prop_width)
#define NS_FONT_VARIANT_EAST_ASIAN_RUBY (1 << eFeatureEastAsian_ruby)
#define NS_FONT_VARIANT_EAST_ASIAN_VARIANT_MASK ( \
NS_FONT_VARIANT_EAST_ASIAN_JIS78 | \
NS_FONT_VARIANT_EAST_ASIAN_JIS83 | \
NS_FONT_VARIANT_EAST_ASIAN_JIS90 | \
NS_FONT_VARIANT_EAST_ASIAN_JIS04 | \
NS_FONT_VARIANT_EAST_ASIAN_SIMPLIFIED | \
NS_FONT_VARIANT_EAST_ASIAN_TRADITIONAL )
#define NS_FONT_VARIANT_EAST_ASIAN_WIDTH_MASK ( \
NS_FONT_VARIANT_EAST_ASIAN_FULL_WIDTH | \
NS_FONT_VARIANT_EAST_ASIAN_PROP_WIDTH )
enum {
eFeatureLigatures_none,
eFeatureLigatures_common,
eFeatureLigatures_no_common,
eFeatureLigatures_discretionary,
eFeatureLigatures_no_discretionary,
eFeatureLigatures_historical,
eFeatureLigatures_no_historical,
eFeatureLigatures_contextual,
eFeatureLigatures_no_contextual,
eFeatureLigatures_numFeatures
};
#define NS_FONT_VARIANT_LIGATURES_NONE (1 << eFeatureLigatures_none)
#define NS_FONT_VARIANT_LIGATURES_COMMON (1 << eFeatureLigatures_common)
#define NS_FONT_VARIANT_LIGATURES_NO_COMMON (1 << eFeatureLigatures_no_common)
#define NS_FONT_VARIANT_LIGATURES_DISCRETIONARY (1 << eFeatureLigatures_discretionary)
#define NS_FONT_VARIANT_LIGATURES_NO_DISCRETIONARY (1 << eFeatureLigatures_no_discretionary)
#define NS_FONT_VARIANT_LIGATURES_HISTORICAL (1 << eFeatureLigatures_historical)
#define NS_FONT_VARIANT_LIGATURES_NO_HISTORICAL (1 << eFeatureLigatures_no_historical)
#define NS_FONT_VARIANT_LIGATURES_CONTEXTUAL (1 << eFeatureLigatures_contextual)
#define NS_FONT_VARIANT_LIGATURES_NO_CONTEXTUAL (1 << eFeatureLigatures_no_contextual)
#define NS_FONT_VARIANT_LIGATURES_COMMON_MASK ( \
NS_FONT_VARIANT_LIGATURES_COMMON | \
NS_FONT_VARIANT_LIGATURES_NO_COMMON )
#define NS_FONT_VARIANT_LIGATURES_DISCRETIONARY_MASK ( \
NS_FONT_VARIANT_LIGATURES_DISCRETIONARY | \
NS_FONT_VARIANT_LIGATURES_NO_DISCRETIONARY )
#define NS_FONT_VARIANT_LIGATURES_HISTORICAL_MASK ( \
NS_FONT_VARIANT_LIGATURES_HISTORICAL | \
NS_FONT_VARIANT_LIGATURES_NO_HISTORICAL )
#define NS_FONT_VARIANT_LIGATURES_CONTEXTUAL_MASK \
NS_FONT_VARIANT_LIGATURES_CONTEXTUAL | \
NS_FONT_VARIANT_LIGATURES_NO_CONTEXTUAL
enum {
eFeatureNumeric_lining,
eFeatureNumeric_oldstyle,
eFeatureNumeric_proportional,
eFeatureNumeric_tabular,
eFeatureNumeric_diagonal_fractions,
eFeatureNumeric_stacked_fractions,
eFeatureNumeric_slashedzero,
eFeatureNumeric_ordinal,
eFeatureNumeric_numFeatures
};
#define NS_FONT_VARIANT_NUMERIC_LINING (1 << eFeatureNumeric_lining)
#define NS_FONT_VARIANT_NUMERIC_OLDSTYLE (1 << eFeatureNumeric_oldstyle)
#define NS_FONT_VARIANT_NUMERIC_PROPORTIONAL (1 << eFeatureNumeric_proportional)
#define NS_FONT_VARIANT_NUMERIC_TABULAR (1 << eFeatureNumeric_tabular)
#define NS_FONT_VARIANT_NUMERIC_DIAGONAL_FRACTIONS (1 << eFeatureNumeric_diagonal_fractions)
#define NS_FONT_VARIANT_NUMERIC_STACKED_FRACTIONS (1 << eFeatureNumeric_stacked_fractions)
#define NS_FONT_VARIANT_NUMERIC_SLASHZERO (1 << eFeatureNumeric_slashedzero)
#define NS_FONT_VARIANT_NUMERIC_ORDINAL (1 << eFeatureNumeric_ordinal)
#define NS_FONT_VARIANT_NUMERIC_FIGURE_MASK \
NS_FONT_VARIANT_NUMERIC_LINING | \
NS_FONT_VARIANT_NUMERIC_OLDSTYLE
#define NS_FONT_VARIANT_NUMERIC_SPACING_MASK \
NS_FONT_VARIANT_NUMERIC_PROPORTIONAL | \
NS_FONT_VARIANT_NUMERIC_TABULAR
#define NS_FONT_VARIANT_NUMERIC_FRACTION_MASK \
NS_FONT_VARIANT_NUMERIC_DIAGONAL_FRACTIONS | \
NS_FONT_VARIANT_NUMERIC_STACKED_FRACTIONS
#define NS_FONT_VARIANT_POSITION_NORMAL 0
#define NS_FONT_VARIANT_POSITION_SUPER 1
#define NS_FONT_VARIANT_POSITION_SUB 2
#define NS_FONT_VARIANT_WIDTH_NORMAL 0
#define NS_FONT_VARIANT_WIDTH_FULL 1
#define NS_FONT_VARIANT_WIDTH_HALF 2
#define NS_FONT_VARIANT_WIDTH_THIRD 3
#define NS_FONT_VARIANT_WIDTH_QUARTER 4
// based on fixed offset values used within WebKit
#define NS_FONT_SUBSCRIPT_OFFSET_RATIO (0.20)
#define NS_FONT_SUPERSCRIPT_OFFSET_RATIO (0.34)
// this roughly corresponds to font-size: smaller behavior
// at smaller sizes <20px the ratio is closer to 0.8 while at
// larger sizes >45px the ratio is closer to 0.667 and in between
// a blend of values is used
#define NS_FONT_SUB_SUPER_SIZE_RATIO_SMALL (0.82)
#define NS_FONT_SUB_SUPER_SIZE_RATIO_LARGE (0.667)
#define NS_FONT_SUB_SUPER_SMALL_SIZE (20.0)
#define NS_FONT_SUB_SUPER_LARGE_SIZE (45.0)
// pref lang id's for font prefs
enum eFontPrefLang {
#define FONT_PREF_LANG(enum_id_, str_, atom_id_) eFontPrefLang_ ## enum_id_
#include "gfxFontPrefLangList.h"
#undef FONT_PREF_LANG
, eFontPrefLang_CJKSet // special code for CJK set
, eFontPrefLang_First = eFontPrefLang_Western
, eFontPrefLang_Last = eFontPrefLang_Others
, eFontPrefLang_Count = (eFontPrefLang_Last - eFontPrefLang_First + 1)
};
#endif

1831
gfx/thebes/gfxFontEntry.cpp Normal file

File diff suppressed because it is too large Load diff

777
gfx/thebes/gfxFontEntry.h Normal file
View file

@ -0,0 +1,777 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_FONTENTRY_H
#define GFX_FONTENTRY_H
#include "gfxTypes.h"
#include "nsString.h"
#include "gfxFontConstants.h"
#include "gfxFontFeatures.h"
#include "gfxFontUtils.h"
#include "nsTArray.h"
#include "nsTHashtable.h"
#include "mozilla/HashFunctions.h"
#include "mozilla/MemoryReporting.h"
#include "nsUnicodeScriptCodes.h"
#include "nsDataHashtable.h"
#include "harfbuzz/hb.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/UniquePtr.h"
typedef struct gr_face gr_face;
#ifdef DEBUG
#include <stdio.h>
#endif
struct gfxFontStyle;
class gfxContext;
class gfxFont;
class gfxFontFamily;
class gfxUserFontData;
class gfxSVGGlyphs;
class FontInfoData;
struct FontListSizes;
class nsIAtom;
namespace mozilla {
class SVGContextPaint;
};
class gfxCharacterMap : public gfxSparseBitSet {
public:
nsrefcnt AddRef() {
NS_PRECONDITION(int32_t(mRefCnt) >= 0, "illegal refcnt");
++mRefCnt;
NS_LOG_ADDREF(this, mRefCnt, "gfxCharacterMap", sizeof(*this));
return mRefCnt;
}
nsrefcnt Release() {
NS_PRECONDITION(0 != mRefCnt, "dup release");
--mRefCnt;
NS_LOG_RELEASE(this, mRefCnt, "gfxCharacterMap");
if (mRefCnt == 0) {
NotifyReleased();
// |this| has been deleted.
return 0;
}
return mRefCnt;
}
gfxCharacterMap() :
mHash(0), mBuildOnTheFly(false), mShared(false)
{ }
explicit gfxCharacterMap(const gfxSparseBitSet& aOther) :
gfxSparseBitSet(aOther),
mHash(0), mBuildOnTheFly(false), mShared(false)
{ }
void CalcHash() { mHash = GetChecksum(); }
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
return gfxSparseBitSet::SizeOfExcludingThis(aMallocSizeOf);
}
// hash of the cmap bitvector
uint32_t mHash;
// if cmap is built on the fly it's never shared
bool mBuildOnTheFly;
// cmap is shared globally
bool mShared;
protected:
void NotifyReleased();
nsAutoRefCnt mRefCnt;
private:
gfxCharacterMap(const gfxCharacterMap&);
gfxCharacterMap& operator=(const gfxCharacterMap&);
};
class gfxFontEntry {
public:
typedef mozilla::gfx::DrawTarget DrawTarget;
typedef mozilla::unicode::Script Script;
NS_INLINE_DECL_REFCOUNTING(gfxFontEntry)
explicit gfxFontEntry(const nsAString& aName, bool aIsStandardFace = false);
// unique name for the face, *not* the family; not necessarily the
// "real" or user-friendly name, may be an internal identifier
const nsString& Name() const { return mName; }
// family name
const nsString& FamilyName() const { return mFamilyName; }
// The following two methods may be relatively expensive, as they
// will (usually, except on Linux) load and parse the 'name' table;
// they are intended only for the font-inspection API, not for
// perf-critical layout/drawing work.
// The "real" name of the face, if available from the font resource;
// returns Name() if nothing better is available.
virtual nsString RealFaceName();
uint16_t Weight() const { return mWeight; }
int16_t Stretch() const { return mStretch; }
bool IsUserFont() const { return mIsDataUserFont || mIsLocalUserFont; }
bool IsLocalUserFont() const { return mIsLocalUserFont; }
bool IsFixedPitch() const { return mFixedPitch; }
bool IsItalic() const { return mStyle == NS_FONT_STYLE_ITALIC; }
bool IsOblique() const { return mStyle == NS_FONT_STYLE_OBLIQUE; }
bool IsUpright() const { return mStyle == NS_FONT_STYLE_NORMAL; }
bool IsBold() const { return mWeight >= 600; } // bold == weights 600 and above
bool IgnoreGDEF() const { return mIgnoreGDEF; }
bool IgnoreGSUB() const { return mIgnoreGSUB; }
// whether a feature is supported by the font (limited to a small set
// of features for which some form of fallback needs to be implemented)
bool SupportsOpenTypeFeature(Script aScript, uint32_t aFeatureTag);
bool SupportsGraphiteFeature(uint32_t aFeatureTag);
// returns a set containing all input glyph ids for a given feature
const hb_set_t*
InputsForOpenTypeFeature(Script aScript, uint32_t aFeatureTag);
virtual bool IsSymbolFont();
virtual bool HasFontTable(uint32_t aTableTag);
inline bool HasGraphiteTables() {
if (!mCheckedForGraphiteTables) {
CheckForGraphiteTables();
mCheckedForGraphiteTables = true;
}
return mHasGraphiteTables;
}
inline bool HasCmapTable() {
if (!mCharacterMap) {
ReadCMAP();
NS_ASSERTION(mCharacterMap, "failed to initialize character map");
}
return mHasCmapTable;
}
inline bool HasCharacter(uint32_t ch) {
if (mCharacterMap && mCharacterMap->test(ch)) {
return true;
}
return TestCharacterMap(ch);
}
virtual bool SkipDuringSystemFallback() { return false; }
nsresult InitializeUVSMap();
uint16_t GetUVSGlyph(uint32_t aCh, uint32_t aVS);
// All concrete gfxFontEntry subclasses (except gfxUserFontEntry) need
// to override this, otherwise the font will never be used as it will
// be considered to support no characters.
// ReadCMAP() must *always* set the mCharacterMap pointer to a valid
// gfxCharacterMap, even if empty, as other code assumes this pointer
// can be safely dereferenced.
virtual nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr);
bool TryGetSVGData(gfxFont* aFont);
bool HasSVGGlyph(uint32_t aGlyphId);
bool GetSVGGlyphExtents(DrawTarget* aDrawTarget, uint32_t aGlyphId,
gfxRect *aResult);
bool RenderSVGGlyph(gfxContext *aContext, uint32_t aGlyphId,
mozilla::SVGContextPaint* aContextPaint);
// Call this when glyph geometry or rendering has changed
// (e.g. animated SVG glyphs)
void NotifyGlyphsChanged();
bool TryGetColorGlyphs();
bool GetColorLayersInfo(uint32_t aGlyphId,
const mozilla::gfx::Color& aDefaultColor,
nsTArray<uint16_t>& layerGlyphs,
nsTArray<mozilla::gfx::Color>& layerColors);
virtual bool MatchesGenericFamily(const nsACString& aGeneric) const {
return true;
}
virtual bool SupportsLangGroup(nsIAtom *aLangGroup) const {
return true;
}
// Access to raw font table data (needed for Harfbuzz):
// returns a pointer to data owned by the fontEntry or the OS,
// which will remain valid until the blob is destroyed.
// The data MUST be treated as read-only; we may be getting a
// reference to a shared system font cache.
//
// The default implementation uses CopyFontTable to get the data
// into a byte array, and maintains a cache of loaded tables.
//
// Subclasses should override this if they can provide more efficient
// access than copying table data into our own buffers.
//
// Get blob that encapsulates a specific font table, or nullptr if
// the table doesn't exist in the font.
//
// Caller is responsible to call hb_blob_destroy() on the returned blob
// (if non-nullptr) when no longer required. For transient access to a
// table, use of AutoTable (below) is generally preferred.
virtual hb_blob_t *GetFontTable(uint32_t aTag);
// Stack-based utility to return a specified table, automatically releasing
// the blob when the AutoTable goes out of scope.
class AutoTable {
public:
AutoTable(gfxFontEntry* aFontEntry, uint32_t aTag)
{
mBlob = aFontEntry->GetFontTable(aTag);
}
~AutoTable() {
if (mBlob) {
hb_blob_destroy(mBlob);
}
}
operator hb_blob_t*() const { return mBlob; }
private:
hb_blob_t* mBlob;
// not implemented:
AutoTable(const AutoTable&) = delete;
AutoTable& operator=(const AutoTable&) = delete;
};
already_AddRefed<gfxFont>
FindOrMakeFont(const gfxFontStyle *aStyle,
bool aNeedsBold,
gfxCharacterMap* aUnicodeRangeMap = nullptr);
// Get an existing font table cache entry in aBlob if it has been
// registered, or return false if not. Callers must call
// hb_blob_destroy on aBlob if true is returned.
//
// Note that some gfxFont implementations may not call this at all,
// if it is more efficient to get the table from the OS at that level.
bool GetExistingFontTable(uint32_t aTag, hb_blob_t** aBlob);
// Elements of aTable are transferred (not copied) to and returned in a
// new hb_blob_t which is registered on the gfxFontEntry, but the initial
// reference is owned by the caller. Removing the last reference
// unregisters the table from the font entry.
//
// Pass nullptr for aBuffer to indicate that the table is not present and
// nullptr will be returned. Also returns nullptr on OOM.
hb_blob_t *ShareFontTableAndGetBlob(uint32_t aTag,
nsTArray<uint8_t>* aTable);
// Get the font's unitsPerEm from the 'head' table, in the case of an
// sfnt resource. Will return kInvalidUPEM for non-sfnt fonts,
// if present on the platform.
uint16_t UnitsPerEm();
enum {
kMinUPEM = 16, // Limits on valid unitsPerEm range, from the
kMaxUPEM = 16384, // OpenType spec
kInvalidUPEM = uint16_t(-1)
};
// Shaper face accessors:
// NOTE that harfbuzz and graphite handle ownership/lifetime of the face
// object in completely different ways.
// Get HarfBuzz face corresponding to this font file.
// Caller must release with hb_face_destroy() when finished with it,
// and the font entry will be notified via ForgetHBFace.
hb_face_t* GetHBFace();
virtual void ForgetHBFace();
// Get Graphite face corresponding to this font file.
// Caller must call gfxFontEntry::ReleaseGrFace when finished with it.
gr_face* GetGrFace();
virtual void ReleaseGrFace(gr_face* aFace);
// Does the font have graphite contextuals that involve the space glyph
// (and therefore we should bypass the word cache)?
bool HasGraphiteSpaceContextuals();
// Release any SVG-glyphs document this font may have loaded.
void DisconnectSVG();
// Called to notify that aFont is being destroyed. Needed when we're tracking
// the fonts belonging to this font entry.
void NotifyFontDestroyed(gfxFont* aFont);
// For memory reporting of the platform font list.
virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontListSizes* aSizes) const;
virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontListSizes* aSizes) const;
// Used for reporting on individual font entries in the user font cache,
// which are not present in the platform font list.
size_t
ComputedSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
// Used when checking for complex script support, to mask off cmap ranges
struct ScriptRange {
uint32_t rangeStart;
uint32_t rangeEnd;
hb_tag_t tags[3]; // one or two OpenType script tags to check,
// plus a NULL terminator
};
bool SupportsScriptInGSUB(const hb_tag_t* aScriptTags);
nsString mName;
nsString mFamilyName;
uint8_t mStyle : 2; // italic/oblique
bool mFixedPitch : 1;
bool mIsValid : 1;
bool mIsBadUnderlineFont : 1;
bool mIsUserFontContainer : 1; // userfont entry
bool mIsDataUserFont : 1; // platform font entry (data)
bool mIsLocalUserFont : 1; // platform font entry (local)
bool mStandardFace : 1;
bool mSymbolFont : 1;
bool mIgnoreGDEF : 1;
bool mIgnoreGSUB : 1;
bool mSVGInitialized : 1;
bool mHasSpaceFeaturesInitialized : 1;
bool mHasSpaceFeatures : 1;
bool mHasSpaceFeaturesKerning : 1;
bool mHasSpaceFeaturesNonKerning : 1;
bool mSkipDefaultFeatureSpaceCheck : 1;
bool mGraphiteSpaceContextualsInitialized : 1;
bool mHasGraphiteSpaceContextuals : 1;
bool mSpaceGlyphIsInvisible : 1;
bool mSpaceGlyphIsInvisibleInitialized : 1;
bool mHasGraphiteTables : 1;
bool mCheckedForGraphiteTables : 1;
bool mHasCmapTable : 1;
bool mGrFaceInitialized : 1;
bool mCheckedForColorGlyph : 1;
// bitvector of substitution space features per script, one each
// for default and non-default features
uint32_t mDefaultSubSpaceFeatures[(int(Script::NUM_SCRIPT_CODES) + 31) / 32];
uint32_t mNonDefaultSubSpaceFeatures[(int(Script::NUM_SCRIPT_CODES) + 31) / 32];
uint16_t mWeight;
int16_t mStretch;
RefPtr<gfxCharacterMap> mCharacterMap;
uint32_t mUVSOffset;
mozilla::UniquePtr<uint8_t[]> mUVSData;
mozilla::UniquePtr<gfxUserFontData> mUserFontData;
mozilla::UniquePtr<gfxSVGGlyphs> mSVGGlyphs;
// list of gfxFonts that are using SVG glyphs
nsTArray<gfxFont*> mFontsUsingSVGGlyphs;
nsTArray<gfxFontFeature> mFeatureSettings;
mozilla::UniquePtr<nsDataHashtable<nsUint32HashKey,bool>> mSupportedFeatures;
mozilla::UniquePtr<nsDataHashtable<nsUint32HashKey,hb_set_t*>> mFeatureInputs;
uint32_t mLanguageOverride;
// Color Layer font support
hb_blob_t* mCOLR;
hb_blob_t* mCPAL;
protected:
friend class gfxPlatformFontList;
friend class gfxMacPlatformFontList;
friend class gfxUserFcFontEntry;
friend class gfxFontFamily;
friend class gfxSingleFaceMacFontFamily;
friend class gfxUserFontEntry;
gfxFontEntry();
// Protected destructor, to discourage deletion outside of Release():
virtual ~gfxFontEntry();
virtual gfxFont *CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold) {
NS_NOTREACHED("oops, somebody didn't override CreateFontInstance");
return nullptr;
}
virtual void CheckForGraphiteTables();
// Copy a font table into aBuffer.
// The caller will be responsible for ownership of the data.
virtual nsresult CopyFontTable(uint32_t aTableTag,
nsTArray<uint8_t>& aBuffer) {
NS_NOTREACHED("forgot to override either GetFontTable or CopyFontTable?");
return NS_ERROR_FAILURE;
}
// lookup the cmap in cached font data
virtual already_AddRefed<gfxCharacterMap>
GetCMAPFromFontInfo(FontInfoData *aFontInfoData,
uint32_t& aUVSOffset,
bool& aSymbolFont);
// helper for HasCharacter(), which is what client code should call
virtual bool TestCharacterMap(uint32_t aCh);
// Font's unitsPerEm from the 'head' table, if available (will be set to
// kInvalidUPEM for non-sfnt font formats)
uint16_t mUnitsPerEm;
// Shaper-specific face objects, shared by all instantiations of the same
// physical font, regardless of size.
// Usually, only one of these will actually be created for any given font
// entry, depending on the font tables that are present.
// hb_face_t is refcounted internally, so each shaper that's using it will
// bump the ref count when it acquires the face, and "destroy" (release) it
// in its destructor. The font entry has only this non-owning reference to
// the face; when the face is deleted, it will tell the font entry to forget
// it, so that a new face will be created next time it is needed.
hb_face_t* mHBFace;
static hb_blob_t* HBGetTable(hb_face_t *face, uint32_t aTag, void *aUserData);
// Callback that the hb_face will use to tell us when it is being deleted.
static void HBFaceDeletedCallback(void *aUserData);
// gr_face is -not- refcounted, so it will be owned directly by the font
// entry, and we'll keep a count of how many references we've handed out;
// each shaper is responsible to call ReleaseGrFace on its entry when
// finished with it, so that we know when it can be deleted.
gr_face* mGrFace;
// hashtable to map raw table data ptr back to its owning blob, for use by
// graphite table-release callback
nsDataHashtable<nsPtrHashKey<const void>,void*>* mGrTableMap;
// number of current users of this entry's mGrFace
nsrefcnt mGrFaceRefCnt;
static const void* GrGetTable(const void *aAppFaceHandle,
unsigned int aName,
size_t *aLen);
static void GrReleaseTable(const void *aAppFaceHandle,
const void *aTableBuffer);
// For memory reporting: size of user-font data belonging to this entry.
// We record this in the font entry because the actual data block may be
// handed over to platform APIs, so that it would become difficult (and
// platform-specific) to measure it directly at report-gathering time.
uint32_t mComputedSizeOfUserFont;
private:
/**
* Font table hashtable, to support GetFontTable for harfbuzz.
*
* The harfbuzz shaper (and potentially other clients) needs access to raw
* font table data. This needs to be cached so that it can be used
* repeatedly (each time we construct a text run; in some cases, for
* each character/glyph within the run) without re-fetching large tables
* every time.
*
* Because we may instantiate many gfxFonts for the same physical font
* file (at different sizes), we should ensure that they can share a
* single cached copy of the font tables. To do this, we implement table
* access and sharing on the fontEntry rather than the font itself.
*
* The default implementation uses GetFontTable() to read font table
* data into byte arrays, and wraps them in blobs which are registered in
* a hashtable. The hashtable can then return pre-existing blobs to
* harfbuzz.
*
* Harfbuzz will "destroy" the blobs when it is finished with them. When
* the last blob reference is removed, the FontTableBlobData user data
* will remove the blob from the hashtable if still registered.
*/
class FontTableBlobData;
/**
* FontTableHashEntry manages the entries of hb_blob_t's containing font
* table data.
*
* This is used to share font tables across fonts with the same
* font entry (but different sizes) for use by HarfBuzz. The hashtable
* does not own a strong reference to the blob, but keeps a weak pointer,
* managed by FontTableBlobData. Similarly FontTableBlobData keeps only a
* weak pointer to the hashtable, managed by FontTableHashEntry.
*/
class FontTableHashEntry : public nsUint32HashKey
{
public:
// Declarations for nsTHashtable
typedef nsUint32HashKey KeyClass;
typedef KeyClass::KeyType KeyType;
typedef KeyClass::KeyTypePointer KeyTypePointer;
explicit FontTableHashEntry(KeyTypePointer aTag)
: KeyClass(aTag)
, mSharedBlobData(nullptr)
, mBlob(nullptr)
{ }
// NOTE: This assumes the new entry belongs to the same hashtable as
// the old, because the mHashtable pointer in mSharedBlobData (if
// present) will not be updated.
FontTableHashEntry(FontTableHashEntry&& toMove)
: KeyClass(mozilla::Move(toMove))
, mSharedBlobData(mozilla::Move(toMove.mSharedBlobData))
, mBlob(mozilla::Move(toMove.mBlob))
{
toMove.mSharedBlobData = nullptr;
toMove.mBlob = nullptr;
}
~FontTableHashEntry() { Clear(); }
// FontTable/Blob API
// Transfer (not copy) elements of aTable to a new hb_blob_t and
// return ownership to the caller. A weak reference to the blob is
// recorded in the hashtable entry so that others may use the same
// table.
hb_blob_t *
ShareTableAndGetBlob(nsTArray<uint8_t>&& aTable,
nsTHashtable<FontTableHashEntry> *aHashtable);
// Return a strong reference to the blob.
// Callers must hb_blob_destroy the returned blob.
hb_blob_t *GetBlob() const;
void Clear();
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
private:
static void DeleteFontTableBlobData(void *aBlobData);
// not implemented
FontTableHashEntry& operator=(FontTableHashEntry& toCopy);
FontTableBlobData *mSharedBlobData;
hb_blob_t *mBlob;
};
mozilla::UniquePtr<nsTHashtable<FontTableHashEntry> > mFontTableCache;
gfxFontEntry(const gfxFontEntry&);
gfxFontEntry& operator=(const gfxFontEntry&);
};
// used when iterating over all fonts looking for a match for a given character
struct GlobalFontMatch {
GlobalFontMatch(const uint32_t aCharacter,
mozilla::unicode::Script aRunScript,
const gfxFontStyle *aStyle) :
mCh(aCharacter), mRunScript(aRunScript), mStyle(aStyle),
mMatchRank(0), mCount(0), mCmapsTested(0)
{
}
const uint32_t mCh; // codepoint to be matched
mozilla::unicode::Script mRunScript; // Unicode script for the codepoint
const gfxFontStyle* mStyle; // style to match
int32_t mMatchRank; // metric indicating closest match
RefPtr<gfxFontEntry> mBestMatch; // current best match
RefPtr<gfxFontFamily> mMatchedFamily; // the family it belongs to
uint32_t mCount; // number of fonts matched
uint32_t mCmapsTested; // number of cmaps tested
};
class gfxFontFamily {
public:
NS_INLINE_DECL_REFCOUNTING(gfxFontFamily)
explicit gfxFontFamily(const nsAString& aName) :
mName(aName),
mOtherFamilyNamesInitialized(false),
mHasOtherFamilyNames(false),
mFaceNamesInitialized(false),
mHasStyles(false),
mIsSimpleFamily(false),
mIsBadUnderlineFamily(false),
mFamilyCharacterMapInitialized(false),
mSkipDefaultFeatureSpaceCheck(false),
mCheckForFallbackFaces(false)
{ }
const nsString& Name() { return mName; }
virtual void LocalizedName(nsAString& aLocalizedName);
virtual bool HasOtherFamilyNames();
nsTArray<RefPtr<gfxFontEntry> >& GetFontList() { return mAvailableFonts; }
void AddFontEntry(RefPtr<gfxFontEntry> aFontEntry) {
// bug 589682 - set the IgnoreGDEF flag on entries for Italic faces
// of Times New Roman, because of buggy table in those fonts
if (aFontEntry->IsItalic() && !aFontEntry->IsUserFont() &&
Name().EqualsLiteral("Times New Roman"))
{
aFontEntry->mIgnoreGDEF = true;
}
if (aFontEntry->mFamilyName.IsEmpty()) {
aFontEntry->mFamilyName = Name();
} else {
MOZ_ASSERT(aFontEntry->mFamilyName.Equals(Name()));
}
aFontEntry->mSkipDefaultFeatureSpaceCheck = mSkipDefaultFeatureSpaceCheck;
mAvailableFonts.AppendElement(aFontEntry);
}
// note that the styles for this family have been added
bool HasStyles() { return mHasStyles; }
void SetHasStyles(bool aHasStyles) { mHasStyles = aHasStyles; }
// choose a specific face to match a style using CSS font matching
// rules (weight matching occurs here). may return a face that doesn't
// precisely match (e.g. normal face when no italic face exists).
// aNeedsSyntheticBold is set to true when synthetic bolding is
// needed, false otherwise
gfxFontEntry *FindFontForStyle(const gfxFontStyle& aFontStyle,
bool& aNeedsSyntheticBold);
virtual void
FindAllFontsForStyle(const gfxFontStyle& aFontStyle,
nsTArray<gfxFontEntry*>& aFontEntryList,
bool& aNeedsSyntheticBold);
// checks for a matching font within the family
// used as part of the font fallback process
void FindFontForChar(GlobalFontMatch *aMatchData);
// checks all fonts for a matching font within the family
void SearchAllFontsForChar(GlobalFontMatch *aMatchData);
// read in other family names, if any, and use functor to add each into cache
virtual void ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList);
// helper method for reading localized family names from the name table
// of a single face
static void ReadOtherFamilyNamesForFace(const nsAString& aFamilyName,
const char *aNameData,
uint32_t aDataLength,
nsTArray<nsString>& aOtherFamilyNames,
bool useFullName);
// set when other family names have been read in
void SetOtherFamilyNamesInitialized() {
mOtherFamilyNamesInitialized = true;
}
// read in other localized family names, fullnames and Postscript names
// for all faces and append to lookup tables
virtual void ReadFaceNames(gfxPlatformFontList *aPlatformFontList,
bool aNeedFullnamePostscriptNames,
FontInfoData *aFontInfoData = nullptr);
// find faces belonging to this family (platform implementations override this;
// should be made pure virtual once all subclasses have been updated)
virtual void FindStyleVariations(FontInfoData *aFontInfoData = nullptr) { }
// search for a specific face using the Postscript name
gfxFontEntry* FindFont(const nsAString& aPostscriptName);
// read in cmaps for all the faces
void ReadAllCMAPs(FontInfoData *aFontInfoData = nullptr);
bool TestCharacterMap(uint32_t aCh) {
if (!mFamilyCharacterMapInitialized) {
ReadAllCMAPs();
}
return mFamilyCharacterMap.test(aCh);
}
void ResetCharacterMap() {
mFamilyCharacterMap.reset();
mFamilyCharacterMapInitialized = false;
}
// mark this family as being in the "bad" underline offset blacklist
void SetBadUnderlineFamily() {
mIsBadUnderlineFamily = true;
if (mHasStyles) {
SetBadUnderlineFonts();
}
}
bool IsBadUnderlineFamily() const { return mIsBadUnderlineFamily; }
bool CheckForFallbackFaces() const { return mCheckForFallbackFaces; }
// sort available fonts to put preferred (standard) faces towards the end
void SortAvailableFonts();
// check whether the family fits into the simple 4-face model,
// so we can use simplified style-matching;
// if so set the mIsSimpleFamily flag (defaults to False before we've checked)
void CheckForSimpleFamily();
// For memory reporter
virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontListSizes* aSizes) const;
virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontListSizes* aSizes) const;
#ifdef DEBUG
// Only used for debugging checks - does a linear search
bool ContainsFace(gfxFontEntry* aFontEntry);
#endif
void SetSkipSpaceFeatureCheck(bool aSkipCheck) {
mSkipDefaultFeatureSpaceCheck = aSkipCheck;
}
protected:
// Protected destructor, to discourage deletion outside of Release():
virtual ~gfxFontFamily()
{
}
bool ReadOtherFamilyNamesForFace(gfxPlatformFontList *aPlatformFontList,
hb_blob_t *aNameTable,
bool useFullName = false);
// set whether this font family is in "bad" underline offset blacklist.
void SetBadUnderlineFonts() {
uint32_t i, numFonts = mAvailableFonts.Length();
for (i = 0; i < numFonts; i++) {
if (mAvailableFonts[i]) {
mAvailableFonts[i]->mIsBadUnderlineFont = true;
}
}
}
nsString mName;
nsTArray<RefPtr<gfxFontEntry> > mAvailableFonts;
gfxSparseBitSet mFamilyCharacterMap;
bool mOtherFamilyNamesInitialized : 1;
bool mHasOtherFamilyNames : 1;
bool mFaceNamesInitialized : 1;
bool mHasStyles : 1;
bool mIsSimpleFamily : 1;
bool mIsBadUnderlineFamily : 1;
bool mFamilyCharacterMapInitialized : 1;
bool mSkipDefaultFeatureSpaceCheck : 1;
bool mCheckForFallbackFaces : 1; // check other faces for character
enum {
// for "simple" families, the faces are stored in mAvailableFonts
// with fixed positions:
kRegularFaceIndex = 0,
kBoldFaceIndex = 1,
kItalicFaceIndex = 2,
kBoldItalicFaceIndex = 3,
// mask values for selecting face with bold and/or italic attributes
kBoldMask = 0x01,
kItalicMask = 0x02
};
};
#endif

View file

@ -0,0 +1,364 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_FONT_FAMILY_LIST_H
#define GFX_FONT_FAMILY_LIST_H
#include "nsDebug.h"
#include "nsISupportsImpl.h"
#include "nsString.h"
#include "nsUnicharUtils.h"
#include "nsTArray.h"
#include "mozilla/MemoryReporting.h"
namespace mozilla {
/**
* type of font family name, either a name (e.g. Helvetica) or a
* generic (e.g. serif, sans-serif), with the ability to distinguish
* between unquoted and quoted names for serializaiton
*/
enum FontFamilyType : uint32_t {
eFamily_none = 0, // used when finding generics
// explicitly named font family (e.g. Helvetica)
eFamily_named,
eFamily_named_quoted,
// generics
eFamily_serif, // pref font code relies on this ordering!!!
eFamily_sans_serif,
eFamily_monospace,
eFamily_cursive,
eFamily_fantasy,
// special
eFamily_moz_variable,
eFamily_moz_fixed,
eFamily_generic_first = eFamily_serif,
eFamily_generic_last = eFamily_fantasy,
eFamily_generic_count = (eFamily_fantasy - eFamily_serif + 1)
};
enum QuotedName { eQuotedName, eUnquotedName };
/**
* font family name, a string for the name if not a generic and
* a font type indicated named family or which generic family
*/
struct FontFamilyName final {
FontFamilyName()
: mType(eFamily_named)
{}
// named font family - e.g. Helvetica
explicit FontFamilyName(const nsAString& aFamilyName,
QuotedName aQuoted = eUnquotedName) {
mType = (aQuoted == eQuotedName) ? eFamily_named_quoted : eFamily_named;
mName = aFamilyName;
}
// generic font family - e.g. sans-serif
explicit FontFamilyName(FontFamilyType aType) {
NS_ASSERTION(aType != eFamily_named &&
aType != eFamily_named_quoted &&
aType != eFamily_none,
"expected a generic font type");
mName.Truncate();
mType = aType;
}
FontFamilyName(const FontFamilyName& aCopy) {
mType = aCopy.mType;
mName = aCopy.mName;
}
bool IsNamed() const {
return mType == eFamily_named || mType == eFamily_named_quoted;
}
bool IsGeneric() const {
return !IsNamed();
}
void AppendToString(nsAString& aFamilyList, bool aQuotes = true) const {
switch (mType) {
case eFamily_named:
aFamilyList.Append(mName);
break;
case eFamily_named_quoted:
if (aQuotes) {
aFamilyList.Append('"');
}
aFamilyList.Append(mName);
if (aQuotes) {
aFamilyList.Append('"');
}
break;
case eFamily_serif:
aFamilyList.AppendLiteral("serif");
break;
case eFamily_sans_serif:
aFamilyList.AppendLiteral("sans-serif");
break;
case eFamily_monospace:
aFamilyList.AppendLiteral("monospace");
break;
case eFamily_cursive:
aFamilyList.AppendLiteral("cursive");
break;
case eFamily_fantasy:
aFamilyList.AppendLiteral("fantasy");
break;
case eFamily_moz_fixed:
aFamilyList.AppendLiteral("-moz-fixed");
break;
default:
break;
}
}
// helper method that converts generic names to the right enum value
static FontFamilyName
Convert(const nsAString& aFamilyOrGenericName) {
// should only be passed a single font - not entirely correct, a family
// *could* have a comma in it but in practice never does so
// for debug purposes this is fine
NS_ASSERTION(aFamilyOrGenericName.FindChar(',') == -1,
"Convert method should only be passed a single family name");
FontFamilyType genericType = eFamily_none;
if (aFamilyOrGenericName.LowerCaseEqualsLiteral("serif")) {
genericType = eFamily_serif;
} else if (aFamilyOrGenericName.LowerCaseEqualsLiteral("sans-serif")) {
genericType = eFamily_sans_serif;
} else if (aFamilyOrGenericName.LowerCaseEqualsLiteral("monospace")) {
genericType = eFamily_monospace;
} else if (aFamilyOrGenericName.LowerCaseEqualsLiteral("cursive")) {
genericType = eFamily_cursive;
} else if (aFamilyOrGenericName.LowerCaseEqualsLiteral("fantasy")) {
genericType = eFamily_fantasy;
} else if (aFamilyOrGenericName.LowerCaseEqualsLiteral("-moz-fixed")) {
genericType = eFamily_moz_fixed;
} else {
return FontFamilyName(aFamilyOrGenericName, eUnquotedName);
}
return FontFamilyName(genericType);
}
// memory reporting
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
return mName.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
}
FontFamilyType mType;
nsString mName; // empty if mType != eFamily_named
};
inline bool
operator==(const FontFamilyName& a, const FontFamilyName& b) {
return a.mType == b.mType && a.mName == b.mName;
}
/**
* font family list, array of font families and a default font type.
* font family names are either named strings or generics. the default
* font type is used to preserve the variable font fallback behavior
*/
class FontFamilyList {
public:
FontFamilyList()
: mDefaultFontType(eFamily_none)
{
}
explicit FontFamilyList(FontFamilyType aGenericType)
: mDefaultFontType(eFamily_none)
{
Append(FontFamilyName(aGenericType));
}
FontFamilyList(const nsAString& aFamilyName,
QuotedName aQuoted)
: mDefaultFontType(eFamily_none)
{
Append(FontFamilyName(aFamilyName, aQuoted));
}
FontFamilyList(const FontFamilyList& aOther)
: mFontlist(aOther.mFontlist)
, mDefaultFontType(aOther.mDefaultFontType)
{
}
void Append(const FontFamilyName& aFamilyName) {
mFontlist.AppendElement(aFamilyName);
}
void Append(const nsTArray<nsString>& aFamilyNameList) {
uint32_t len = aFamilyNameList.Length();
for (uint32_t i = 0; i < len; i++) {
mFontlist.AppendElement(FontFamilyName(aFamilyNameList[i],
eUnquotedName));
}
}
void Clear() {
mFontlist.Clear();
}
uint32_t Length() const {
return mFontlist.Length();
}
bool IsEmpty() const {
return mFontlist.IsEmpty();
}
const nsTArray<FontFamilyName>& GetFontlist() const {
return mFontlist;
}
bool Equals(const FontFamilyList& aFontlist) const {
return mFontlist == aFontlist.mFontlist &&
mDefaultFontType == aFontlist.mDefaultFontType;
}
FontFamilyType FirstGeneric() const {
uint32_t len = mFontlist.Length();
for (uint32_t i = 0; i < len; i++) {
const FontFamilyName& name = mFontlist[i];
if (name.IsGeneric()) {
return name.mType;
}
}
return eFamily_none;
}
bool HasGeneric() const {
return FirstGeneric() != eFamily_none;
}
bool HasDefaultGeneric() const {
uint32_t len = mFontlist.Length();
for (uint32_t i = 0; i < len; i++) {
const FontFamilyName& name = mFontlist[i];
if (name.mType == mDefaultFontType) {
return true;
}
}
return false;
}
// Find the first generic (but ignoring cursive and fantasy, as they are
// rarely configured in any useful way) in the list.
// If found, move it to the start and return true; else return false.
bool PrioritizeFirstGeneric() {
uint32_t len = mFontlist.Length();
for (uint32_t i = 0; i < len; i++) {
const FontFamilyName name = mFontlist[i];
if (name.IsGeneric()) {
if (name.mType == eFamily_cursive ||
name.mType == eFamily_fantasy) {
continue;
}
if (i > 0) {
mFontlist.RemoveElementAt(i);
mFontlist.InsertElementAt(0, name);
}
return true;
}
}
return false;
}
void PrependGeneric(FontFamilyType aType) {
mFontlist.InsertElementAt(0, FontFamilyName(aType));
}
void ToString(nsAString& aFamilyList,
bool aQuotes = true,
bool aIncludeDefault = false) const {
aFamilyList.Truncate();
uint32_t len = mFontlist.Length();
for (uint32_t i = 0; i < len; i++) {
if (i != 0) {
aFamilyList.Append(',');
}
const FontFamilyName& name = mFontlist[i];
name.AppendToString(aFamilyList, aQuotes);
}
if (aIncludeDefault && mDefaultFontType != eFamily_none) {
if (!aFamilyList.IsEmpty()) {
aFamilyList.Append(',');
}
if (mDefaultFontType == eFamily_serif) {
aFamilyList.AppendLiteral("serif");
} else {
aFamilyList.AppendLiteral("sans-serif");
}
}
}
// searches for a specific non-generic name, lowercase comparison
bool Contains(const nsAString& aFamilyName) const {
uint32_t len = mFontlist.Length();
nsAutoString fam(aFamilyName);
ToLowerCase(fam);
for (uint32_t i = 0; i < len; i++) {
const FontFamilyName& name = mFontlist[i];
if (name.mType != eFamily_named &&
name.mType != eFamily_named_quoted) {
continue;
}
nsAutoString listname(name.mName);
ToLowerCase(listname);
if (listname.Equals(fam)) {
return true;
}
}
return false;
}
FontFamilyType GetDefaultFontType() const { return mDefaultFontType; }
void SetDefaultFontType(FontFamilyType aType) {
NS_ASSERTION(aType == eFamily_none || aType == eFamily_serif ||
aType == eFamily_sans_serif,
"default font type must be either serif or sans-serif");
mDefaultFontType = aType;
}
// memory reporting
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
size_t n = 0;
n += mFontlist.ShallowSizeOfExcludingThis(aMallocSizeOf);
for (size_t i = 0; i < mFontlist.Length(); i++) {
n += mFontlist[i].SizeOfExcludingThis(aMallocSizeOf);
}
return n;
}
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
}
private:
nsTArray<FontFamilyName> mFontlist;
FontFamilyType mDefaultFontType; // none, serif or sans-serif
};
inline bool
operator==(const FontFamilyList& a, const FontFamilyList& b) {
return a.Equals(b);
}
} // namespace mozilla
#endif /* GFX_FONT_FAMILY_LIST_H */

View file

@ -0,0 +1,81 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gfxFontFeatures.h"
#include "nsUnicharUtils.h"
#include "nsHashKeys.h"
using namespace mozilla;
gfxFontFeatureValueSet::gfxFontFeatureValueSet()
: mFontFeatureValues(8)
{
}
bool
gfxFontFeatureValueSet::GetFontFeatureValuesFor(const nsAString& aFamily,
uint32_t aVariantProperty,
const nsAString& aName,
nsTArray<uint32_t>& aValues)
{
nsAutoString family(aFamily), name(aName);
ToLowerCase(family);
ToLowerCase(name);
FeatureValueHashKey key(family, aVariantProperty, name);
aValues.Clear();
FeatureValueHashEntry *entry = mFontFeatureValues.GetEntry(key);
if (entry) {
NS_ASSERTION(entry->mValues.Length() > 0,
"null array of font feature values");
aValues.AppendElements(entry->mValues);
return true;
}
return false;
}
void
gfxFontFeatureValueSet::AddFontFeatureValues(const nsAString& aFamily,
const nsTArray<gfxFontFeatureValueSet::FeatureValues>& aValues)
{
nsAutoString family(aFamily);
ToLowerCase(family);
uint32_t i, numFeatureValues = aValues.Length();
for (i = 0; i < numFeatureValues; i++) {
const FeatureValues& fv = aValues.ElementAt(i);
uint32_t alternate = fv.alternate;
uint32_t j, numValues = fv.valuelist.Length();
for (j = 0; j < numValues; j++) {
const ValueList& v = fv.valuelist.ElementAt(j);
nsAutoString name(v.name);
ToLowerCase(name);
FeatureValueHashKey key(family, alternate, name);
FeatureValueHashEntry *entry = mFontFeatureValues.PutEntry(key);
entry->mKey = key;
entry->mValues = v.featureSelectors;
}
}
}
bool
gfxFontFeatureValueSet::FeatureValueHashEntry::KeyEquals(
const KeyTypePointer aKey) const
{
return aKey->mPropVal == mKey.mPropVal &&
aKey->mFamily.Equals(mKey.mFamily) &&
aKey->mName.Equals(mKey.mName);
}
PLDHashNumber
gfxFontFeatureValueSet::FeatureValueHashEntry::HashKey(
const KeyTypePointer aKey)
{
return HashString(aKey->mFamily) + HashString(aKey->mName) +
aKey->mPropVal * uint32_t(0xdeadbeef);
}

View file

@ -0,0 +1,126 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_FONT_FEATURES_H
#define GFX_FONT_FEATURES_H
#include "nsTHashtable.h"
#include "nsTArray.h"
#include "nsString.h"
// An OpenType feature tag and value pair
struct gfxFontFeature {
uint32_t mTag; // see http://www.microsoft.com/typography/otspec/featuretags.htm
uint32_t mValue; // 0 = off, 1 = on, larger values may be used as parameters
// to features that select among multiple alternatives
};
inline bool
operator<(const gfxFontFeature& a, const gfxFontFeature& b)
{
return (a.mTag < b.mTag) || ((a.mTag == b.mTag) && (a.mValue < b.mValue));
}
inline bool
operator==(const gfxFontFeature& a, const gfxFontFeature& b)
{
return (a.mTag == b.mTag) && (a.mValue == b.mValue);
}
struct gfxAlternateValue {
uint32_t alternate; // constants in gfxFontConstants.h
nsString value; // string value to be looked up
};
inline bool
operator<(const gfxAlternateValue& a, const gfxAlternateValue& b)
{
return (a.alternate < b.alternate) ||
((a.alternate == b.alternate) && (a.value < b.value));
}
inline bool
operator==(const gfxAlternateValue& a, const gfxAlternateValue& b)
{
return (a.alternate == b.alternate) && (a.value == b.value);
}
class gfxFontFeatureValueSet final {
public:
NS_INLINE_DECL_REFCOUNTING(gfxFontFeatureValueSet)
gfxFontFeatureValueSet();
struct ValueList {
ValueList(const nsAString& aName, const nsTArray<uint32_t>& aSelectors)
: name(aName), featureSelectors(aSelectors)
{}
nsString name;
nsTArray<uint32_t> featureSelectors;
};
struct FeatureValues {
uint32_t alternate;
nsTArray<ValueList> valuelist;
};
// returns true if found, false otherwise
bool
GetFontFeatureValuesFor(const nsAString& aFamily,
uint32_t aVariantProperty,
const nsAString& aName,
nsTArray<uint32_t>& aValues);
void
AddFontFeatureValues(const nsAString& aFamily,
const nsTArray<gfxFontFeatureValueSet::FeatureValues>& aValues);
private:
// Private destructor, to discourage deletion outside of Release():
~gfxFontFeatureValueSet() {}
struct FeatureValueHashKey {
nsString mFamily;
uint32_t mPropVal;
nsString mName;
FeatureValueHashKey()
: mPropVal(0)
{ }
FeatureValueHashKey(const nsAString& aFamily,
uint32_t aPropVal,
const nsAString& aName)
: mFamily(aFamily), mPropVal(aPropVal), mName(aName)
{ }
FeatureValueHashKey(const FeatureValueHashKey& aKey)
: mFamily(aKey.mFamily), mPropVal(aKey.mPropVal), mName(aKey.mName)
{ }
};
class FeatureValueHashEntry : public PLDHashEntryHdr {
public:
typedef const FeatureValueHashKey &KeyType;
typedef const FeatureValueHashKey *KeyTypePointer;
explicit FeatureValueHashEntry(KeyTypePointer aKey) { }
FeatureValueHashEntry(const FeatureValueHashEntry& toCopy)
{
NS_ERROR("Should not be called");
}
~FeatureValueHashEntry() { }
bool KeyEquals(const KeyTypePointer aKey) const;
static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
static PLDHashNumber HashKey(const KeyTypePointer aKey);
enum { ALLOW_MEMMOVE = true };
FeatureValueHashKey mKey;
nsTArray<uint32_t> mValues;
};
nsTHashtable<FeatureValueHashEntry> mFontFeatureValues;
};
#endif

View file

@ -0,0 +1,281 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gfxFontInfoLoader.h"
#include "nsCRT.h"
#include "nsIObserverService.h"
#include "nsThreadUtils.h" // for nsRunnable
#include "gfxPlatformFontList.h"
using namespace mozilla;
using services::GetObserverService;
#define LOG_FONTINIT(args) MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontinit), \
LogLevel::Debug, args)
#define LOG_FONTINIT_ENABLED() MOZ_LOG_TEST( \
gfxPlatform::GetLog(eGfxLog_fontinit), \
LogLevel::Debug)
void
FontInfoData::Load()
{
TimeStamp start = TimeStamp::Now();
uint32_t i, n = mFontFamiliesToLoad.Length();
mLoadStats.families = n;
for (i = 0; i < n && !mCanceled; i++) {
// font file memory mapping sometimes causes exceptions - bug 1100949
MOZ_SEH_TRY {
LoadFontFamilyData(mFontFamiliesToLoad[i]);
} MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
gfxCriticalError() <<
"Exception occurred reading font data for " <<
NS_ConvertUTF16toUTF8(mFontFamiliesToLoad[i]).get();
}
}
mLoadTime = TimeStamp::Now() - start;
}
class FontInfoLoadCompleteEvent : public Runnable {
virtual ~FontInfoLoadCompleteEvent() {}
NS_DECL_ISUPPORTS_INHERITED
explicit FontInfoLoadCompleteEvent(FontInfoData *aFontInfo) :
mFontInfo(aFontInfo)
{}
NS_IMETHOD Run() override;
RefPtr<FontInfoData> mFontInfo;
};
class AsyncFontInfoLoader : public Runnable {
virtual ~AsyncFontInfoLoader() {}
NS_DECL_ISUPPORTS_INHERITED
explicit AsyncFontInfoLoader(FontInfoData *aFontInfo) :
mFontInfo(aFontInfo)
{
mCompleteEvent = new FontInfoLoadCompleteEvent(aFontInfo);
}
NS_IMETHOD Run() override;
RefPtr<FontInfoData> mFontInfo;
RefPtr<FontInfoLoadCompleteEvent> mCompleteEvent;
};
class ShutdownThreadEvent : public Runnable {
virtual ~ShutdownThreadEvent() {}
NS_DECL_ISUPPORTS_INHERITED
explicit ShutdownThreadEvent(nsIThread* aThread) : mThread(aThread) {}
NS_IMETHOD Run() override {
mThread->Shutdown();
return NS_OK;
}
nsCOMPtr<nsIThread> mThread;
};
NS_IMPL_ISUPPORTS_INHERITED0(ShutdownThreadEvent, Runnable);
// runs on main thread after async font info loading is done
nsresult
FontInfoLoadCompleteEvent::Run()
{
gfxFontInfoLoader *loader =
static_cast<gfxFontInfoLoader*>(gfxPlatformFontList::PlatformFontList());
loader->FinalizeLoader(mFontInfo);
return NS_OK;
}
NS_IMPL_ISUPPORTS_INHERITED0(FontInfoLoadCompleteEvent, Runnable);
// runs on separate thread
nsresult
AsyncFontInfoLoader::Run()
{
// load platform-specific font info
mFontInfo->Load();
// post a completion event that transfer the data to the fontlist
NS_DispatchToMainThread(mCompleteEvent);
return NS_OK;
}
NS_IMPL_ISUPPORTS_INHERITED0(AsyncFontInfoLoader, Runnable);
NS_IMPL_ISUPPORTS(gfxFontInfoLoader::ShutdownObserver, nsIObserver)
NS_IMETHODIMP
gfxFontInfoLoader::ShutdownObserver::Observe(nsISupports *aSubject,
const char *aTopic,
const char16_t *someData)
{
if (!nsCRT::strcmp(aTopic, "quit-application")) {
mLoader->CancelLoader();
} else {
NS_NOTREACHED("unexpected notification topic");
}
return NS_OK;
}
void
gfxFontInfoLoader::StartLoader(uint32_t aDelay, uint32_t aInterval)
{
mInterval = aInterval;
NS_ASSERTION(!mFontInfo,
"fontinfo should be null when starting font loader");
// sanity check
if (mState != stateInitial &&
mState != stateTimerOff &&
mState != stateTimerOnDelay) {
CancelLoader();
}
// set up timer
if (!mTimer) {
mTimer = do_CreateInstance("@mozilla.org/timer;1");
if (!mTimer) {
NS_WARNING("Failure to create font info loader timer");
return;
}
}
AddShutdownObserver();
// delay? ==> start async thread after a delay
if (aDelay) {
mState = stateTimerOnDelay;
mTimer->InitWithFuncCallback(DelayedStartCallback, this, aDelay,
nsITimer::TYPE_ONE_SHOT);
return;
}
mFontInfo = CreateFontInfoData();
// initialize
InitLoader();
// start async load
nsresult rv = NS_NewNamedThread("Font Loader",
getter_AddRefs(mFontLoaderThread),
nullptr);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
mState = stateAsyncLoad;
nsCOMPtr<nsIRunnable> loadEvent = new AsyncFontInfoLoader(mFontInfo);
mFontLoaderThread->Dispatch(loadEvent.forget(), NS_DISPATCH_NORMAL);
if (LOG_FONTINIT_ENABLED()) {
LOG_FONTINIT(("(fontinit) fontloader started (fontinfo: %p)\n",
mFontInfo.get()));
}
}
void
gfxFontInfoLoader::FinalizeLoader(FontInfoData *aFontInfo)
{
// Avoid loading data if loader has already been canceled.
// This should mean that CancelLoader() ran and the Load
// thread has already Shutdown(), and likely before processing
// the Shutdown event it handled the load event and sent back
// our Completion event, thus we end up here.
if (mState != stateAsyncLoad || mFontInfo != aFontInfo) {
return;
}
mLoadTime = mFontInfo->mLoadTime;
// try to load all font data immediately
if (LoadFontInfo()) {
CancelLoader();
return;
}
// not all work completed ==> run load on interval
mState = stateTimerOnInterval;
mTimer->InitWithFuncCallback(LoadFontInfoCallback, this, mInterval,
nsITimer::TYPE_REPEATING_SLACK);
}
void
gfxFontInfoLoader::CancelLoader()
{
if (mState == stateInitial) {
return;
}
mState = stateTimerOff;
if (mTimer) {
mTimer->Cancel();
mTimer = nullptr;
}
if (mFontInfo) // null during any initial delay
mFontInfo->mCanceled = true;
if (mFontLoaderThread) {
NS_DispatchToMainThread(new ShutdownThreadEvent(mFontLoaderThread));
mFontLoaderThread = nullptr;
}
RemoveShutdownObserver();
CleanupLoader();
}
void
gfxFontInfoLoader::LoadFontInfoTimerFire()
{
if (mState == stateTimerOnDelay) {
mState = stateTimerOnInterval;
mTimer->SetDelay(mInterval);
}
bool done = LoadFontInfo();
if (done) {
CancelLoader();
}
}
gfxFontInfoLoader::~gfxFontInfoLoader()
{
RemoveShutdownObserver();
MOZ_COUNT_DTOR(gfxFontInfoLoader);
}
void
gfxFontInfoLoader::AddShutdownObserver()
{
if (mObserver) {
return;
}
nsCOMPtr<nsIObserverService> obs = GetObserverService();
if (obs) {
mObserver = new ShutdownObserver(this);
obs->AddObserver(mObserver, "quit-application", false);
}
}
void
gfxFontInfoLoader::RemoveShutdownObserver()
{
if (mObserver) {
nsCOMPtr<nsIObserverService> obs = GetObserverService();
if (obs) {
obs->RemoveObserver(mObserver, "quit-application");
mObserver = nullptr;
}
}
}

View file

@ -0,0 +1,258 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_FONT_INFO_LOADER_H
#define GFX_FONT_INFO_LOADER_H
#include "nsCOMPtr.h"
#include "nsIObserver.h"
#include "nsITimer.h"
#include "nsIThread.h"
#include "nsRefPtrHashtable.h"
#include "nsString.h"
#include "gfxFont.h"
#include "nsIRunnable.h"
#include "mozilla/Atomics.h"
#include "mozilla/TimeStamp.h"
#include "nsISupportsImpl.h"
// data retrieved for a given face
struct FontFaceData {
FontFaceData() : mUVSOffset(0), mSymbolFont(false) {}
FontFaceData(const FontFaceData& aFontFaceData) {
mFullName = aFontFaceData.mFullName;
mPostscriptName = aFontFaceData.mPostscriptName;
mCharacterMap = aFontFaceData.mCharacterMap;
mUVSOffset = aFontFaceData.mUVSOffset;
mSymbolFont = aFontFaceData.mSymbolFont;
}
nsString mFullName;
nsString mPostscriptName;
RefPtr<gfxCharacterMap> mCharacterMap;
uint32_t mUVSOffset;
bool mSymbolFont;
};
// base class used to contain cached system-wide font info.
// methods in this class are called on off-main threads so
// all methods use only static methods or other thread-safe
// font data access API's. specifically, no use is made of
// gfxPlatformFontList, gfxFontFamily, gfxFamily or any
// harfbuzz API methods within FontInfoData subclasses.
class FontInfoData {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FontInfoData)
FontInfoData(bool aLoadOtherNames,
bool aLoadFaceNames,
bool aLoadCmaps) :
mCanceled(false),
mLoadOtherNames(aLoadOtherNames),
mLoadFaceNames(aLoadFaceNames),
mLoadCmaps(aLoadCmaps)
{
MOZ_COUNT_CTOR(FontInfoData);
}
protected:
// Protected destructor, to discourage deletion outside of Release():
virtual ~FontInfoData() {
MOZ_COUNT_DTOR(FontInfoData);
}
public:
virtual void Load();
// loads font data for all fonts of a given family
// (called on async thread)
virtual void LoadFontFamilyData(const nsAString& aFamilyName) = 0;
// -- methods overriden by platform-specific versions --
// fetches cmap data for a particular font from cached font data
virtual already_AddRefed<gfxCharacterMap>
GetCMAP(const nsAString& aFontName,
uint32_t& aUVSOffset,
bool& aSymbolFont)
{
FontFaceData faceData;
if (!mFontFaceData.Get(aFontName, &faceData) ||
!faceData.mCharacterMap) {
return nullptr;
}
aUVSOffset = faceData.mUVSOffset;
aSymbolFont = faceData.mSymbolFont;
RefPtr<gfxCharacterMap> cmap = faceData.mCharacterMap;
return cmap.forget();
}
// fetches fullname/postscript names from cached font data
virtual void GetFaceNames(const nsAString& aFontName,
nsAString& aFullName,
nsAString& aPostscriptName)
{
FontFaceData faceData;
if (!mFontFaceData.Get(aFontName, &faceData)) {
return;
}
aFullName = faceData.mFullName;
aPostscriptName = faceData.mPostscriptName;
}
// fetches localized family name data from cached font data
virtual bool GetOtherFamilyNames(const nsAString& aFamilyName,
nsTArray<nsString>& aOtherFamilyNames)
{
return mOtherFamilyNames.Get(aFamilyName, &aOtherFamilyNames);
}
nsTArray<nsString> mFontFamiliesToLoad;
// currently non-issue but beware,
// this is also set during cleanup after finishing
mozilla::Atomic<bool> mCanceled;
// time spent on the loader thread
mozilla::TimeDuration mLoadTime;
struct FontCounts {
uint32_t families;
uint32_t fonts;
uint32_t cmaps;
uint32_t facenames;
uint32_t othernames;
};
FontCounts mLoadStats;
bool mLoadOtherNames;
bool mLoadFaceNames;
bool mLoadCmaps;
// face name ==> per-face data
nsDataHashtable<nsStringHashKey, FontFaceData> mFontFaceData;
// canonical family name ==> array of localized family names
nsDataHashtable<nsStringHashKey, nsTArray<nsString> > mOtherFamilyNames;
};
// gfxFontInfoLoader - helper class for loading font info on async thread
// For large, "all fonts on system" data, data needed on a given platform
// (e.g. localized names, face names, cmaps) are loaded async.
// helper class for loading in font info on a separate async thread
// once async thread completes, completion process is run on regular
// intervals to prevent tying up the main thread
class gfxFontInfoLoader {
public:
// state transitions:
// initial ---StartLoader with delay---> timer on delay
// initial ---StartLoader without delay---> timer on interval
// timer on delay ---LoaderTimerFire---> timer on interval
// timer on delay ---CancelLoader---> timer off
// timer on interval ---CancelLoader---> timer off
// timer off ---StartLoader with delay---> timer on delay
// timer off ---StartLoader without delay---> timer on interval
typedef enum {
stateInitial,
stateTimerOnDelay,
stateAsyncLoad,
stateTimerOnInterval,
stateTimerOff
} TimerState;
gfxFontInfoLoader() :
mInterval(0), mState(stateInitial)
{
MOZ_COUNT_CTOR(gfxFontInfoLoader);
}
virtual ~gfxFontInfoLoader();
// start timer with an initial delay, then call Run method at regular intervals
void StartLoader(uint32_t aDelay, uint32_t aInterval);
// Finalize - async load complete, transfer data (on intervals if necessary)
virtual void FinalizeLoader(FontInfoData *aFontInfo);
// cancel the timer and cleanup
void CancelLoader();
uint32_t GetInterval() { return mInterval; }
protected:
class ShutdownObserver : public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
explicit ShutdownObserver(gfxFontInfoLoader *aLoader)
: mLoader(aLoader)
{ }
protected:
virtual ~ShutdownObserver()
{ }
gfxFontInfoLoader *mLoader;
};
// CreateFontInfo - create platform-specific object used
// to load system-wide font info
virtual already_AddRefed<FontInfoData> CreateFontInfoData() {
return nullptr;
}
// Init - initialization before async loader thread runs
virtual void InitLoader() = 0;
// LoadFontInfo - transfer font info data within a time limit, return
// true when done
virtual bool LoadFontInfo() = 0;
// Cleanup - finish and cleanup after done, including possible reflows
virtual void CleanupLoader() {
mFontInfo = nullptr;
}
// Timer interval callbacks
static void LoadFontInfoCallback(nsITimer *aTimer, void *aThis) {
gfxFontInfoLoader *loader = static_cast<gfxFontInfoLoader*>(aThis);
loader->LoadFontInfoTimerFire();
}
static void DelayedStartCallback(nsITimer *aTimer, void *aThis) {
gfxFontInfoLoader *loader = static_cast<gfxFontInfoLoader*>(aThis);
loader->StartLoader(0, loader->GetInterval());
}
void LoadFontInfoTimerFire();
void AddShutdownObserver();
void RemoveShutdownObserver();
nsCOMPtr<nsITimer> mTimer;
nsCOMPtr<nsIObserver> mObserver;
nsCOMPtr<nsIThread> mFontLoaderThread;
uint32_t mInterval;
TimerState mState;
// after async font loader completes, data is stored here
RefPtr<FontInfoData> mFontInfo;
// time spent on the loader thread
mozilla::TimeDuration mLoadTime;
};
#endif /* GFX_FONT_INFO_LOADER_H */

View file

@ -0,0 +1,288 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gfxFontMissingGlyphs.h"
#include "gfxUtils.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/Helpers.h"
#include "mozilla/gfx/PathHelpers.h"
#include "mozilla/RefPtr.h"
#include "nsDeviceContext.h"
#include "nsLayoutUtils.h"
using namespace mozilla;
using namespace mozilla::gfx;
#define CHAR_BITS(b00, b01, b02, b10, b11, b12, b20, b21, b22, b30, b31, b32, b40, b41, b42) \
((b00 << 0) | (b01 << 1) | (b02 << 2) | (b10 << 3) | (b11 << 4) | (b12 << 5) | \
(b20 << 6) | (b21 << 7) | (b22 << 8) | (b30 << 9) | (b31 << 10) | (b32 << 11) | \
(b40 << 12) | (b41 << 13) | (b42 << 14))
static const uint16_t glyphMicroFont[16] = {
CHAR_BITS(0, 1, 0,
1, 0, 1,
1, 0, 1,
1, 0, 1,
0, 1, 0),
CHAR_BITS(0, 1, 0,
0, 1, 0,
0, 1, 0,
0, 1, 0,
0, 1, 0),
CHAR_BITS(1, 1, 1,
0, 0, 1,
1, 1, 1,
1, 0, 0,
1, 1, 1),
CHAR_BITS(1, 1, 1,
0, 0, 1,
1, 1, 1,
0, 0, 1,
1, 1, 1),
CHAR_BITS(1, 0, 1,
1, 0, 1,
1, 1, 1,
0, 0, 1,
0, 0, 1),
CHAR_BITS(1, 1, 1,
1, 0, 0,
1, 1, 1,
0, 0, 1,
1, 1, 1),
CHAR_BITS(1, 1, 1,
1, 0, 0,
1, 1, 1,
1, 0, 1,
1, 1, 1),
CHAR_BITS(1, 1, 1,
0, 0, 1,
0, 0, 1,
0, 0, 1,
0, 0, 1),
CHAR_BITS(0, 1, 0,
1, 0, 1,
0, 1, 0,
1, 0, 1,
0, 1, 0),
CHAR_BITS(1, 1, 1,
1, 0, 1,
1, 1, 1,
0, 0, 1,
0, 0, 1),
CHAR_BITS(1, 1, 1,
1, 0, 1,
1, 1, 1,
1, 0, 1,
1, 0, 1),
CHAR_BITS(1, 1, 0,
1, 0, 1,
1, 1, 0,
1, 0, 1,
1, 1, 0),
CHAR_BITS(0, 1, 1,
1, 0, 0,
1, 0, 0,
1, 0, 0,
0, 1, 1),
CHAR_BITS(1, 1, 0,
1, 0, 1,
1, 0, 1,
1, 0, 1,
1, 1, 0),
CHAR_BITS(1, 1, 1,
1, 0, 0,
1, 1, 1,
1, 0, 0,
1, 1, 1),
CHAR_BITS(1, 1, 1,
1, 0, 0,
1, 1, 1,
1, 0, 0,
1, 0, 0)
};
/* Parameters that control the rendering of hexboxes. They look like this:
BMP codepoints non-BMP codepoints
(U+0000 - U+FFFF) (U+10000 - U+10FFFF)
+---------+ +-------------+
| | | |
| HHH HHH | | HHH HHH HHH |
| HHH HHH | | HHH HHH HHH |
| HHH HHH | | HHH HHH HHH |
| HHH HHH | | HHH HHH HHH |
| HHH HHH | | HHH HHH HHH |
| | | |
| HHH HHH | | HHH HHH HHH |
| HHH HHH | | HHH HHH HHH |
| HHH HHH | | HHH HHH HHH |
| HHH HHH | | HHH HHH HHH |
| HHH HHH | | HHH HHH HHH |
| | | |
+---------+ +-------------+
*/
/** Width of a minifont glyph (see above) */
static const int MINIFONT_WIDTH = 3;
/** Height of a minifont glyph (see above) */
static const int MINIFONT_HEIGHT = 5;
/**
* Gap between minifont glyphs (both horizontal and vertical) and also
* the minimum desired gap between the box border and the glyphs
*/
static const int HEX_CHAR_GAP = 1;
/**
* The amount of space between the vertical edge of the glyphbox and the
* box border. We make this nonzero so that when multiple missing glyphs
* occur consecutively there's a gap between their rendered boxes.
*/
static const int BOX_HORIZONTAL_INSET = 1;
/** The width of the border */
static const int BOX_BORDER_WIDTH = 1;
/**
* The scaling factor for the border opacity; this is multiplied by the current
* opacity being used to draw the text.
*/
static const Float BOX_BORDER_OPACITY = 0.5;
/**
* Draw a single hex character using the current color. A nice way to do this
* would be to fill in an A8 image surface and then use it as a mask
* to paint the current color. Tragically this doesn't currently work with the
* Quartz cairo backend which doesn't generally support masking with surfaces.
* So for now we just paint a bunch of rectangles...
*/
#ifndef MOZ_GFX_OPTIMIZE_MOBILE
static void
DrawHexChar(uint32_t aDigit, const Point& aPt, DrawTarget& aDrawTarget,
const Pattern &aPattern)
{
// To avoid the potential for seams showing between rects when we're under
// a transform we concat all the rects into a PathBuilder and fill the
// resulting Path (rather than using DrawTarget::FillRect).
RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder();
uint32_t glyphBits = glyphMicroFont[aDigit];
for (int y = 0; y < MINIFONT_HEIGHT; ++y) {
for (int x = 0; x < MINIFONT_WIDTH; ++x) {
if (glyphBits & 1) {
Rect r(aPt.x + x, aPt.y + y, 1, 1);
MaybeSnapToDevicePixels(r, aDrawTarget, true);
builder->MoveTo(r.TopLeft());
builder->LineTo(r.TopRight());
builder->LineTo(r.BottomRight());
builder->LineTo(r.BottomLeft());
builder->Close();
}
glyphBits >>= 1;
}
}
RefPtr<Path> path = builder->Finish();
aDrawTarget.Fill(path, aPattern);
}
#endif // MOZ_GFX_OPTIMIZE_MOBILE
void
gfxFontMissingGlyphs::DrawMissingGlyph(uint32_t aChar,
const Rect& aRect,
DrawTarget& aDrawTarget,
const Pattern& aPattern,
uint32_t aAppUnitsPerDevPixel)
{
// If we're currently drawing with some kind of pattern, we just draw the
// missing-glyph data in black.
ColorPattern color = aPattern.GetType() == PatternType::COLOR ?
static_cast<const ColorPattern&>(aPattern) :
ColorPattern(ToDeviceColor(Color(0.f, 0.f, 0.f, 1.f)));
// Stroke a rectangle so that the stroke's left edge is inset one pixel
// from the left edge of the glyph box and the stroke's right edge
// is inset one pixel from the right edge of the glyph box.
Float halfBorderWidth = BOX_BORDER_WIDTH / 2.0;
Float borderLeft = aRect.X() + BOX_HORIZONTAL_INSET + halfBorderWidth;
Float borderRight = aRect.XMost() - BOX_HORIZONTAL_INSET - halfBorderWidth;
Rect borderStrokeRect(borderLeft, aRect.Y() + halfBorderWidth,
borderRight - borderLeft,
aRect.Height() - 2.0 * halfBorderWidth);
if (!borderStrokeRect.IsEmpty()) {
ColorPattern adjustedColor = color;
color.mColor.a *= BOX_BORDER_OPACITY;
#ifdef MOZ_GFX_OPTIMIZE_MOBILE
aDrawTarget.FillRect(borderStrokeRect, adjustedColor);
#else
StrokeOptions strokeOptions(BOX_BORDER_WIDTH);
aDrawTarget.StrokeRect(borderStrokeRect, adjustedColor, strokeOptions);
#endif
}
#ifndef MOZ_GFX_OPTIMIZE_MOBILE
Point center = aRect.Center();
Float halfGap = HEX_CHAR_GAP / 2.f;
Float top = -(MINIFONT_HEIGHT + halfGap);
// We always want integer scaling, otherwise the "bitmap" glyphs will look
// even uglier than usual when zoomed
int32_t devPixelsPerCSSPx =
std::max<int32_t>(1, nsDeviceContext::AppUnitsPerCSSPixel() /
aAppUnitsPerDevPixel);
AutoRestoreTransform autoRestoreTransform(&aDrawTarget);
aDrawTarget.SetTransform(
aDrawTarget.GetTransform().PreTranslate(center).
PreScale(devPixelsPerCSSPx,
devPixelsPerCSSPx));
if (aChar < 0x10000) {
if (aRect.Width() >= 2 * (MINIFONT_WIDTH + HEX_CHAR_GAP) &&
aRect.Height() >= 2 * MINIFONT_HEIGHT + HEX_CHAR_GAP) {
// Draw 4 digits for BMP
Float left = -(MINIFONT_WIDTH + halfGap);
DrawHexChar((aChar >> 12) & 0xF,
Point(left, top), aDrawTarget, color);
DrawHexChar((aChar >> 8) & 0xF,
Point(halfGap, top), aDrawTarget, color);
DrawHexChar((aChar >> 4) & 0xF,
Point(left, halfGap), aDrawTarget, color);
DrawHexChar(aChar & 0xF,
Point(halfGap, halfGap), aDrawTarget, color);
}
} else {
if (aRect.Width() >= 3 * (MINIFONT_WIDTH + HEX_CHAR_GAP) &&
aRect.Height() >= 2 * MINIFONT_HEIGHT + HEX_CHAR_GAP) {
// Draw 6 digits for non-BMP
Float first = -(MINIFONT_WIDTH * 1.5 + HEX_CHAR_GAP);
Float second = -(MINIFONT_WIDTH / 2.0);
Float third = (MINIFONT_WIDTH / 2.0 + HEX_CHAR_GAP);
DrawHexChar((aChar >> 20) & 0xF,
Point(first, top), aDrawTarget, color);
DrawHexChar((aChar >> 16) & 0xF,
Point(second, top), aDrawTarget, color);
DrawHexChar((aChar >> 12) & 0xF,
Point(third, top), aDrawTarget, color);
DrawHexChar((aChar >> 8) & 0xF,
Point(first, halfGap), aDrawTarget, color);
DrawHexChar((aChar >> 4) & 0xF,
Point(second, halfGap), aDrawTarget, color);
DrawHexChar(aChar & 0xF,
Point(third, halfGap), aDrawTarget, color);
}
}
#endif
}
Float
gfxFontMissingGlyphs::GetDesiredMinWidth(uint32_t aChar,
uint32_t aAppUnitsPerDevPixel)
{
/**
* The minimum desired width for a missing-glyph glyph box. I've laid it out
* like this so you can see what goes where.
*/
Float width = BOX_HORIZONTAL_INSET + BOX_BORDER_WIDTH + HEX_CHAR_GAP +
MINIFONT_WIDTH + HEX_CHAR_GAP + MINIFONT_WIDTH +
((aChar < 0x10000) ? 0 : HEX_CHAR_GAP + MINIFONT_WIDTH) +
HEX_CHAR_GAP + BOX_BORDER_WIDTH + BOX_HORIZONTAL_INSET;
// Note that this will give us floating-point division, so the width will
// -not- be snapped to integer multiples of its basic pixel value
width *= Float(nsDeviceContext::AppUnitsPerCSSPixel()) / aAppUnitsPerDevPixel;
return width;
}

View file

@ -0,0 +1,55 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_FONTMISSINGGLYPHS_H
#define GFX_FONTMISSINGGLYPHS_H
#include "mozilla/Attributes.h"
#include "mozilla/gfx/Rect.h"
namespace mozilla {
namespace gfx {
class DrawTarget;
class Pattern;
} // namespace gfx
} // namespace mozilla
/**
* This class should not be instantiated. It's just a container
* for some helper functions.
*/
class gfxFontMissingGlyphs final
{
typedef mozilla::gfx::DrawTarget DrawTarget;
typedef mozilla::gfx::Float Float;
typedef mozilla::gfx::Pattern Pattern;
typedef mozilla::gfx::Rect Rect;
gfxFontMissingGlyphs() = delete; // prevent instantiation
public:
/**
* Draw hexboxes for a missing glyph.
* @param aChar the UTF16 codepoint for the character
* @param aRect the glyph-box for the glyph that is missing
* @param aDrawTarget the DrawTarget to draw to
* @param aPattern the pattern currently being used to paint
* @param aAppUnitsPerDevPixel the appUnits to devPixel ratio we're using,
* (so we can scale glyphs to a sensible size)
*/
static void DrawMissingGlyph(uint32_t aChar,
const Rect& aRect,
DrawTarget& aDrawTarget,
const Pattern& aPattern,
uint32_t aAppUnitsPerDevPixel);
/**
* @return the desired minimum width for a glyph-box that will allow
* the hexboxes to be drawn reasonably.
*/
static Float GetDesiredMinWidth(uint32_t aChar,
uint32_t aAppUnitsPerDevUnit);
};
#endif

View file

@ -0,0 +1,36 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// this needs to match the list of pref font.default.xx entries listed in all.js!
FONT_PREF_LANG(Western, "x-western", x_western),
FONT_PREF_LANG(Japanese, "ja", Japanese),
FONT_PREF_LANG(ChineseTW, "zh-TW", Taiwanese),
FONT_PREF_LANG(ChineseCN, "zh-CN", Chinese),
FONT_PREF_LANG(ChineseHK, "zh-HK", HongKongChinese),
FONT_PREF_LANG(Korean, "ko", ko),
FONT_PREF_LANG(Cyrillic, "x-cyrillic", x_cyrillic),
FONT_PREF_LANG(Greek, "el", el),
FONT_PREF_LANG(Thai, "th", th),
FONT_PREF_LANG(Hebrew, "he", he),
FONT_PREF_LANG(Arabic, "ar", ar),
FONT_PREF_LANG(Devanagari, "x-devanagari", x_devanagari),
FONT_PREF_LANG(Tamil, "x-tamil", x_tamil),
FONT_PREF_LANG(Armenian, "x-armn", x_armn),
FONT_PREF_LANG(Bengali, "x-beng", x_beng),
FONT_PREF_LANG(Canadian, "x-cans", x_cans),
FONT_PREF_LANG(Ethiopic, "x-ethi", x_ethi),
FONT_PREF_LANG(Georgian, "x-geor", x_geor),
FONT_PREF_LANG(Gujarati, "x-gujr", x_gujr),
FONT_PREF_LANG(Gurmukhi, "x-guru", x_guru),
FONT_PREF_LANG(Khmer, "x-khmr", x_khmr),
FONT_PREF_LANG(Malayalam, "x-mlym", x_mlym),
FONT_PREF_LANG(Mathematics, "x-math", x_math),
FONT_PREF_LANG(Oriya, "x-orya", x_orya),
FONT_PREF_LANG(Telugu, "x-telu", x_telu),
FONT_PREF_LANG(Kannada, "x-knda", x_knda),
FONT_PREF_LANG(Sinhala, "x-sinh", x_sinh),
FONT_PREF_LANG(Tibetan, "x-tibt", x_tibt),
FONT_PREF_LANG(Others, "x-unicode", Unicode)

View file

@ -0,0 +1,8 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gfxFontTest.h"
gfxFontTestStore* gfxFontTestStore::sCurrentStore = nullptr;

84
gfx/thebes/gfxFontTest.h Normal file
View file

@ -0,0 +1,84 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_FONT_TEST_H
#define GFX_FONT_TEST_H
#include "nsString.h"
#include "nsTArray.h"
#include "cairo.h"
struct gfxFontTestItem {
gfxFontTestItem(const nsCString& fontName,
cairo_glyph_t *cglyphs, int nglyphs)
: platformFont(fontName)
{
glyphs = new cairo_glyph_t[nglyphs];
memcpy (glyphs, cglyphs, sizeof(cairo_glyph_t) * nglyphs);
num_glyphs = nglyphs;
}
gfxFontTestItem(const gfxFontTestItem& other) {
platformFont = other.platformFont;
num_glyphs = other.num_glyphs;
glyphs = new cairo_glyph_t[num_glyphs];
memcpy (glyphs, other.glyphs, sizeof(cairo_glyph_t) * num_glyphs);
}
~gfxFontTestItem() {
delete [] glyphs;
}
nsCString platformFont;
cairo_glyph_t *glyphs;
int num_glyphs;
};
class gfxFontTestStore {
public:
gfxFontTestStore() { }
void AddItem (const nsCString& fontString,
cairo_glyph_t *cglyphs, int nglyphs)
{
items.AppendElement(gfxFontTestItem(fontString, cglyphs, nglyphs));
}
void AddItem (const nsString& fontString,
cairo_glyph_t *cglyphs, int nglyphs)
{
items.AppendElement(gfxFontTestItem(NS_ConvertUTF16toUTF8(fontString), cglyphs, nglyphs));
}
nsTArray<gfxFontTestItem> items;
public:
static gfxFontTestStore *CurrentStore() {
return sCurrentStore;
}
static gfxFontTestStore *NewStore() {
if (sCurrentStore)
delete sCurrentStore;
sCurrentStore = new gfxFontTestStore;
return sCurrentStore;
}
static void DeleteStore() {
if (sCurrentStore)
delete sCurrentStore;
sCurrentStore = nullptr;
}
protected:
static gfxFontTestStore *sCurrentStore;
};
#endif /* GFX_FONT_TEST_H */

1809
gfx/thebes/gfxFontUtils.cpp Normal file

File diff suppressed because it is too large Load diff

1027
gfx/thebes/gfxFontUtils.h Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,124 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_FONTCONFIG_FONTS_H
#define GFX_FONTCONFIG_FONTS_H
#include "cairo.h"
#include "gfxTypes.h"
#include "gfxTextRun.h"
#include "nsAutoRef.h"
#include "nsTArray.h"
#include <pango/pango.h>
class gfxFcFontSet;
class gfxFcFont;
typedef struct _FcPattern FcPattern;
typedef struct FT_FaceRec_* FT_Face;
typedef struct FT_LibraryRec_ *FT_Library;
class gfxPangoFontGroup : public gfxFontGroup {
public:
gfxPangoFontGroup(const mozilla::FontFamilyList& aFontFamilyList,
const gfxFontStyle *aStyle,
gfxUserFontSet *aUserFontSet,
gfxFloat aDevToCssSize);
virtual ~gfxPangoFontGroup();
virtual gfxFontGroup *Copy(const gfxFontStyle *aStyle);
virtual gfxFont* GetFirstValidFont(uint32_t aCh = 0x20);
virtual void UpdateUserFonts();
virtual already_AddRefed<gfxFont>
FindFontForChar(uint32_t aCh, uint32_t aPrevCh, uint32_t aNextCh,
Script aRunScript, gfxFont *aPrevMatchedFont,
uint8_t *aMatchType);
static void Shutdown();
// Used for @font-face { src: local(); }
static gfxFontEntry *NewFontEntry(const nsAString& aFontName,
uint16_t aWeight,
int16_t aStretch,
uint8_t aStyle);
// Used for @font-face { src: url(); }
static gfxFontEntry *NewFontEntry(const nsAString& aFontName,
uint16_t aWeight,
int16_t aStretch,
uint8_t aStyle,
const uint8_t* aFontData,
uint32_t aLength);
private:
virtual gfxFont *GetFontAt(int32_t i, uint32_t aCh = 0x20);
// @param aLang [in] language to use for pref fonts and system default font
// selection, or nullptr for the language guessed from the
// gfxFontStyle.
// The FontGroup holds a reference to this set.
gfxFcFontSet *GetFontSet(PangoLanguage *aLang = nullptr);
class FontSetByLangEntry {
public:
FontSetByLangEntry(PangoLanguage *aLang, gfxFcFontSet *aFontSet);
PangoLanguage *mLang;
RefPtr<gfxFcFontSet> mFontSet;
};
// There is only one of entry in this array unless characters from scripts
// of other languages are measured.
AutoTArray<FontSetByLangEntry,1> mFontSets;
gfxFloat mSizeAdjustFactor;
PangoLanguage *mPangoLanguage;
// @param aLang [in] language to use for pref fonts and system font
// resolution, or nullptr to guess a language from the gfxFontStyle.
// @param aMatchPattern [out] if non-nullptr, will return the pattern used.
already_AddRefed<gfxFcFontSet>
MakeFontSet(PangoLanguage *aLang, gfxFloat aSizeAdjustFactor,
nsAutoRef<FcPattern> *aMatchPattern = nullptr);
gfxFcFontSet *GetBaseFontSet();
gfxFcFont *GetBaseFont();
gfxFloat GetSizeAdjustFactor()
{
if (mFontSets.Length() == 0)
GetBaseFontSet();
return mSizeAdjustFactor;
}
// old helper methods from gfxFontGroup, moved here so that those methods
// can be revamped without affecting the legacy code here
// iterate over the fontlist, lookup names and expand generics
void EnumerateFontListPFG(nsIAtom *aLanguage, void *aClosure);
// expand a generic to a list of specific names based on prefs
void FindGenericFontsPFG(mozilla::FontFamilyType aGenericType,
nsIAtom *aLanguage,
void *aClosure);
// lookup and add a font with a given name (i.e. *not* a generic!)
void FindPlatformFontPFG(const nsAString& aName,
bool aUseFontSet,
void *aClosure);
static void
ResolveGenericFontNamesPFG(mozilla::FontFamilyType aGenericType,
nsIAtom *aLanguage,
nsTArray<nsString>& aGenericFamilies);
friend class gfxSystemFcFontEntry;
static FT_Library GetFTLibrary();
};
#endif /* GFX_FONTCONFIG_FONTS_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,330 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_FONTCONFIG_UTILS_H
#define GFX_FONTCONFIG_UTILS_H
#include "gfxPlatform.h"
#include "mozilla/MathAlgorithms.h"
#include "nsAutoRef.h"
#include "nsTArray.h"
#include "nsTHashtable.h"
#include "nsISupportsImpl.h"
#include "gfxFT2FontBase.h"
#include <fontconfig/fontconfig.h>
template <>
class nsAutoRefTraits<FcPattern> : public nsPointerRefTraits<FcPattern>
{
public:
static void Release(FcPattern *ptr) { FcPatternDestroy(ptr); }
static void AddRef(FcPattern *ptr) { FcPatternReference(ptr); }
};
template <>
class nsAutoRefTraits<FcFontSet> : public nsPointerRefTraits<FcFontSet>
{
public:
static void Release(FcFontSet *ptr) { FcFontSetDestroy(ptr); }
};
template <>
class nsAutoRefTraits<FcCharSet> : public nsPointerRefTraits<FcCharSet>
{
public:
static void Release(FcCharSet *ptr) { FcCharSetDestroy(ptr); }
};
class gfxIgnoreCaseCStringComparator
{
public:
bool Equals(const nsACString& a, const nsACString& b) const
{
return nsCString(a).Equals(b, nsCaseInsensitiveCStringComparator());
}
bool LessThan(const nsACString& a, const nsACString& b) const
{
return a < b;
}
};
class gfxFontconfigUtils {
public:
gfxFontconfigUtils();
static gfxFontconfigUtils* GetFontconfigUtils() {
if (!sUtils)
sUtils = new gfxFontconfigUtils();
return sUtils;
}
static void Shutdown();
nsresult GetFontList(nsIAtom *aLangGroup,
const nsACString& aGenericFamily,
nsTArray<nsString>& aListOfFonts);
nsresult UpdateFontList();
nsresult GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName);
const nsTArray< nsCountedRef<FcPattern> >&
GetFontsForFamily(const FcChar8 *aFamilyName);
const nsTArray< nsCountedRef<FcPattern> >&
GetFontsForFullname(const FcChar8 *aFullname);
// Returns the best support that any font offers for |aLang|.
FcLangResult GetBestLangSupport(const FcChar8 *aLang);
// Returns the fonts offering this best level of support.
const nsTArray< nsCountedRef<FcPattern> >&
GetFontsForLang(const FcChar8 *aLang);
// Retuns the language support for a fontconfig font pattern
static FcLangResult GetLangSupport(FcPattern *aFont, const FcChar8 *aLang);
// Conversions between FcChar8*, which is unsigned char*,
// and (signed) char*, that check the type of the argument.
static const FcChar8 *ToFcChar8(const char *aCharPtr)
{
return reinterpret_cast<const FcChar8*>(aCharPtr);
}
static const FcChar8 *ToFcChar8(const nsCString& aCString)
{
return ToFcChar8(aCString.get());
}
static const char *ToCString(const FcChar8 *aChar8Ptr)
{
return reinterpret_cast<const char*>(aChar8Ptr);
}
static uint8_t FcSlantToThebesStyle(int aFcSlant);
static uint8_t GetThebesStyle(FcPattern *aPattern); // slant
static uint16_t GetThebesWeight(FcPattern *aPattern);
static int16_t GetThebesStretch(FcPattern *aPattern);
static int GetFcSlant(const gfxFontStyle& aFontStyle);
// Returns a precise FC_WEIGHT from |aBaseWeight|,
// which is a CSS absolute weight / 100.
static int FcWeightForBaseWeight(int8_t aBaseWeight);
static int FcWidthForThebesStretch(int16_t aStretch);
static bool GetFullnameFromFamilyAndStyle(FcPattern *aFont,
nsACString *aFullname);
// This doesn't consider which faces exist, and so initializes the pattern
// using a guessed weight, and doesn't consider sizeAdjust.
static nsReturnRef<FcPattern>
NewPattern(const nsTArray<nsString>& aFamilies,
const gfxFontStyle& aFontStyle, const char *aLang);
/**
* @param aLangGroup [in] a Mozilla langGroup.
* @param aFcLang [out] returns a language suitable for fontconfig
* matching |aLangGroup| or an empty string if no match is found.
*/
static void GetSampleLangForGroup(nsIAtom *aLangGroup,
nsACString *aFcLang);
protected:
// Base class for hash table entries with case-insensitive FcChar8
// string keys.
class FcStrEntryBase : public PLDHashEntryHdr {
public:
typedef const FcChar8 *KeyType;
typedef const FcChar8 *KeyTypePointer;
static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; }
// Case-insensitive hash.
//
// fontconfig always ignores case of ASCII characters in family
// names and languages, but treatment of whitespace in families is
// not consistent. FcFontSort and FcFontMatch ignore whitespace
// except for whitespace in the first character, while FcFontList
// and config subsitution tests require whitespace to match
// exactly. CSS 2.1 implies that whitespace is important in the
// font-family property. FcStrCmpIgnoreCase considers whitespace
// important.
static PLDHashNumber HashKey(const FcChar8 *aKey) {
uint32_t hash = 0;
for (const FcChar8 *c = aKey; *c != '\0'; ++c) {
hash = mozilla::RotateLeft(hash, 3) ^ FcToLower(*c);
}
return hash;
}
enum { ALLOW_MEMMOVE = true };
};
public:
// Hash entry with a dependent const FcChar8* pointer to an external
// string for a key (and no data). The user must ensure that the string
// associated with the pointer is not destroyed. This entry type is
// useful for family name keys as the family name string is held in the
// font pattern.
class DepFcStrEntry : public FcStrEntryBase {
public:
// When constructing a new entry in the hashtable, the key is left
// nullptr. The caller of PutEntry() must fill in mKey when nullptr.
// This provides a mechanism for the caller of PutEntry() to determine
// whether the entry has been initialized.
explicit DepFcStrEntry(KeyTypePointer aName)
: mKey(nullptr) { }
DepFcStrEntry(const DepFcStrEntry& toCopy)
: mKey(toCopy.mKey) { }
bool KeyEquals(KeyTypePointer aKey) const {
return FcStrCmpIgnoreCase(aKey, mKey) == 0;
}
const FcChar8 *mKey;
};
// Hash entry that uses a copy of an FcChar8 string to store the key.
// This entry type is useful for language keys, as languages are usually
// not stored as strings in font patterns.
class CopiedFcStrEntry : public FcStrEntryBase {
public:
// When constructing a new entry in the hashtable, the key is void.
// The caller of PutEntry() must call InitKey() when IsKeyInitialized()
// returns false. This provides a mechanism for the caller of
// PutEntry() to determine whether the entry has been initialized.
explicit CopiedFcStrEntry(KeyTypePointer aName) {
mKey.SetIsVoid(true);
}
CopiedFcStrEntry(const CopiedFcStrEntry& toCopy)
: mKey(toCopy.mKey) { }
bool KeyEquals(KeyTypePointer aKey) const {
return FcStrCmpIgnoreCase(aKey, ToFcChar8(mKey)) == 0;
}
bool IsKeyInitialized() { return !mKey.IsVoid(); }
void InitKey(const FcChar8* aKey) { mKey.Assign(ToCString(aKey)); }
private:
nsCString mKey;
};
protected:
class FontsByFcStrEntry : public DepFcStrEntry {
public:
explicit FontsByFcStrEntry(KeyTypePointer aName)
: DepFcStrEntry(aName) { }
FontsByFcStrEntry(const FontsByFcStrEntry& toCopy)
: DepFcStrEntry(toCopy), mFonts(toCopy.mFonts) { }
bool AddFont(FcPattern *aFont) {
return mFonts.AppendElement(aFont) != nullptr;
}
const nsTArray< nsCountedRef<FcPattern> >& GetFonts() {
return mFonts;
}
private:
nsTArray< nsCountedRef<FcPattern> > mFonts;
};
// FontsByFullnameEntry is similar to FontsByFcStrEntry (used for
// mFontsByFamily) except for two differences:
//
// * The font does not always contain a single string for the fullname, so
// the key is sometimes a combination of family and style.
//
// * There is usually only one font.
class FontsByFullnameEntry : public DepFcStrEntry {
public:
// When constructing a new entry in the hashtable, the key is left
// nullptr. The caller of PutEntry() is must fill in mKey when adding
// the first font if the key is not derived from the family and style.
// If the key is derived from family and style, a font must be added.
explicit FontsByFullnameEntry(KeyTypePointer aName)
: DepFcStrEntry(aName) { }
FontsByFullnameEntry(const FontsByFullnameEntry& toCopy)
: DepFcStrEntry(toCopy), mFonts(toCopy.mFonts) { }
bool KeyEquals(KeyTypePointer aKey) const;
bool AddFont(FcPattern *aFont) {
return mFonts.AppendElement(aFont) != nullptr;
}
const nsTArray< nsCountedRef<FcPattern> >& GetFonts() {
return mFonts;
}
// Don't memmove the AutoTArray.
enum { ALLOW_MEMMOVE = false };
private:
// There is usually only one font, but sometimes more.
AutoTArray<nsCountedRef<FcPattern>,1> mFonts;
};
class LangSupportEntry : public CopiedFcStrEntry {
public:
explicit LangSupportEntry(KeyTypePointer aName)
: CopiedFcStrEntry(aName) { }
LangSupportEntry(const LangSupportEntry& toCopy)
: CopiedFcStrEntry(toCopy), mSupport(toCopy.mSupport) { }
FcLangResult mSupport;
nsTArray< nsCountedRef<FcPattern> > mFonts;
};
static gfxFontconfigUtils* sUtils;
bool IsExistingFamily(const nsCString& aFamilyName);
nsresult GetFontListInternal(nsTArray<nsCString>& aListOfFonts,
nsIAtom *aLangGroup);
nsresult UpdateFontListInternal(bool aForce = false);
void AddFullnameEntries();
LangSupportEntry *GetLangSupportEntry(const FcChar8 *aLang,
bool aWithFonts);
// mFontsByFamily and mFontsByFullname contain entries only for families
// and fullnames for which there are fonts.
nsTHashtable<FontsByFcStrEntry> mFontsByFamily;
nsTHashtable<FontsByFullnameEntry> mFontsByFullname;
// mLangSupportTable contains an entry for each language that has been
// looked up through GetLangSupportEntry, even when the language is not
// supported.
nsTHashtable<LangSupportEntry> mLangSupportTable;
const nsTArray< nsCountedRef<FcPattern> > mEmptyPatternArray;
FcConfig *mLastConfig;
#ifdef MOZ_BUNDLED_FONTS
void ActivateBundledFonts();
nsCString mBundledFontsPath;
bool mBundledFontsInitialized;
#endif
};
class gfxFontconfigFontBase : public gfxFT2FontBase {
public:
gfxFontconfigFontBase(cairo_scaled_font_t *aScaledFont,
FcPattern *aPattern,
gfxFontEntry *aFontEntry,
const gfxFontStyle *aFontStyle);
virtual FontType GetType() const override { return FONT_TYPE_FONTCONFIG; }
virtual FcPattern *GetPattern() const { return mPattern; }
private:
nsCountedRef<FcPattern> mPattern;
};
#endif /* GFX_FONTCONFIG_UTILS_H */

564
gfx/thebes/gfxGDIFont.cpp Normal file
View file

@ -0,0 +1,564 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gfxGDIFont.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Sprintf.h"
#include "mozilla/WindowsVersion.h"
#include <algorithm>
#include "gfxWindowsPlatform.h"
#include "gfxContext.h"
#include "mozilla/Preferences.h"
#include "nsUnicodeProperties.h"
#include "gfxFontConstants.h"
#include "gfxTextRun.h"
#include "cairo-win32.h"
#define ROUND(x) floor((x) + 0.5)
using namespace mozilla;
using namespace mozilla::unicode;
static inline cairo_antialias_t
GetCairoAntialiasOption(gfxFont::AntialiasOption anAntialiasOption)
{
switch (anAntialiasOption) {
default:
case gfxFont::kAntialiasDefault:
return CAIRO_ANTIALIAS_DEFAULT;
case gfxFont::kAntialiasNone:
return CAIRO_ANTIALIAS_NONE;
case gfxFont::kAntialiasGrayscale:
return CAIRO_ANTIALIAS_GRAY;
case gfxFont::kAntialiasSubpixel:
return CAIRO_ANTIALIAS_SUBPIXEL;
}
}
gfxGDIFont::gfxGDIFont(GDIFontEntry *aFontEntry,
const gfxFontStyle *aFontStyle,
bool aNeedsBold,
AntialiasOption anAAOption)
: gfxFont(aFontEntry, aFontStyle, anAAOption),
mFont(nullptr),
mFontFace(nullptr),
mMetrics(nullptr),
mSpaceGlyph(0),
mNeedsBold(aNeedsBold),
mScriptCache(nullptr)
{
Initialize();
}
gfxGDIFont::~gfxGDIFont()
{
if (mScaledFont) {
cairo_scaled_font_destroy(mScaledFont);
}
if (mFontFace) {
cairo_font_face_destroy(mFontFace);
}
if (mFont) {
::DeleteObject(mFont);
}
if (mScriptCache) {
ScriptFreeCache(&mScriptCache);
}
delete mMetrics;
}
gfxFont*
gfxGDIFont::CopyWithAntialiasOption(AntialiasOption anAAOption)
{
return new gfxGDIFont(static_cast<GDIFontEntry*>(mFontEntry.get()),
&mStyle, mNeedsBold, anAAOption);
}
bool
gfxGDIFont::ShapeText(DrawTarget *aDrawTarget,
const char16_t *aText,
uint32_t aOffset,
uint32_t aLength,
Script aScript,
bool aVertical,
gfxShapedText *aShapedText)
{
if (!mIsValid) {
NS_WARNING("invalid font! expect incorrect text rendering");
return false;
}
// Ensure the cairo font is set up, so there's no risk it'll fall back to
// creating a "toy" font internally (see bug 544617).
// We must check that this succeeded, otherwise we risk cairo creating the
// wrong kind of font internally as a fallback (bug 744480).
if (!SetupCairoFont(aDrawTarget)) {
return false;
}
return gfxFont::ShapeText(aDrawTarget, aText, aOffset, aLength, aScript,
aVertical, aShapedText);
}
const gfxFont::Metrics&
gfxGDIFont::GetHorizontalMetrics()
{
return *mMetrics;
}
uint32_t
gfxGDIFont::GetSpaceGlyph()
{
return mSpaceGlyph;
}
bool
gfxGDIFont::SetupCairoFont(DrawTarget* aDrawTarget)
{
if (!mScaledFont ||
cairo_scaled_font_status(mScaledFont) != CAIRO_STATUS_SUCCESS) {
// Don't cairo_set_scaled_font as that would propagate the error to
// the cairo_t, precluding any further drawing.
return false;
}
cairo_set_scaled_font(gfxFont::RefCairo(aDrawTarget), mScaledFont);
return true;
}
gfxFont::RunMetrics
gfxGDIFont::Measure(const gfxTextRun *aTextRun,
uint32_t aStart, uint32_t aEnd,
BoundingBoxType aBoundingBoxType,
DrawTarget *aRefDrawTarget,
Spacing *aSpacing,
uint16_t aOrientation)
{
gfxFont::RunMetrics metrics =
gfxFont::Measure(aTextRun, aStart, aEnd, aBoundingBoxType,
aRefDrawTarget, aSpacing, aOrientation);
// if aBoundingBoxType is LOOSE_INK_EXTENTS
// and the underlying cairo font may be antialiased,
// we can't trust Windows to have considered all the pixels
// so we need to add "padding" to the bounds.
// (see bugs 475968, 439831, compare also bug 445087)
if (aBoundingBoxType == LOOSE_INK_EXTENTS &&
mAntialiasOption != kAntialiasNone &&
metrics.mBoundingBox.width > 0) {
metrics.mBoundingBox.x -= aTextRun->GetAppUnitsPerDevUnit();
metrics.mBoundingBox.width += aTextRun->GetAppUnitsPerDevUnit() * 3;
}
return metrics;
}
void
gfxGDIFont::Initialize()
{
NS_ASSERTION(!mMetrics, "re-creating metrics? this will leak");
LOGFONTW logFont;
// Figure out if we want to do synthetic oblique styling.
GDIFontEntry* fe = static_cast<GDIFontEntry*>(GetFontEntry());
bool wantFakeItalic = mStyle.style != NS_FONT_STYLE_NORMAL &&
fe->IsUpright() && mStyle.allowSyntheticStyle;
// If the font's family has an actual italic face (but font matching
// didn't choose it), we have to use a cairo transform instead of asking
// GDI to italicize, because that would use a different face and result
// in a possible glyph ID mismatch between shaping and rendering.
//
// We use the mFamilyHasItalicFace flag in the entry in case of user fonts,
// where the *CSS* family may not know about italic faces that are present
// in the *GDI* family, and which GDI would use if we asked it to perform
// the "italicization".
bool useCairoFakeItalic = wantFakeItalic && fe->mFamilyHasItalicFace;
if (mAdjustedSize == 0.0) {
mAdjustedSize = mStyle.size;
if (mStyle.sizeAdjust > 0.0 && mAdjustedSize > 0.0) {
// to implement font-size-adjust, we first create the "unadjusted" font
FillLogFont(logFont, mAdjustedSize,
wantFakeItalic && !useCairoFakeItalic);
mFont = ::CreateFontIndirectW(&logFont);
// initialize its metrics so we can calculate size adjustment
Initialize();
// Unless the font was so small that GDI metrics rounded to zero,
// calculate the properly adjusted size, and then proceed
// to recreate mFont and recalculate metrics
if (mMetrics->xHeight > 0.0 && mMetrics->emHeight > 0.0) {
gfxFloat aspect = mMetrics->xHeight / mMetrics->emHeight;
mAdjustedSize = mStyle.GetAdjustedSize(aspect);
}
// delete the temporary font and metrics
::DeleteObject(mFont);
mFont = nullptr;
delete mMetrics;
mMetrics = nullptr;
} else if (mStyle.sizeAdjust == 0.0) {
mAdjustedSize = 0.0;
}
}
// (bug 724231) for local user fonts, we don't use GDI's synthetic bold,
// as it could lead to a different, incompatible face being used
// but instead do our own multi-striking
if (mNeedsBold && GetFontEntry()->IsLocalUserFont()) {
mApplySyntheticBold = true;
}
// this may end up being zero
mAdjustedSize = ROUND(mAdjustedSize);
FillLogFont(logFont, mAdjustedSize, wantFakeItalic && !useCairoFakeItalic);
mFont = ::CreateFontIndirectW(&logFont);
mMetrics = new gfxFont::Metrics;
::memset(mMetrics, 0, sizeof(*mMetrics));
AutoDC dc;
SetGraphicsMode(dc.GetDC(), GM_ADVANCED);
AutoSelectFont selectFont(dc.GetDC(), mFont);
// Get font metrics if size > 0
if (mAdjustedSize > 0.0) {
OUTLINETEXTMETRIC oMetrics;
TEXTMETRIC& metrics = oMetrics.otmTextMetrics;
if (0 < GetOutlineTextMetrics(dc.GetDC(), sizeof(oMetrics), &oMetrics)) {
mMetrics->strikeoutSize = (double)oMetrics.otmsStrikeoutSize;
mMetrics->strikeoutOffset = (double)oMetrics.otmsStrikeoutPosition;
mMetrics->underlineSize = (double)oMetrics.otmsUnderscoreSize;
mMetrics->underlineOffset = (double)oMetrics.otmsUnderscorePosition;
const MAT2 kIdentityMatrix = { {0, 1}, {0, 0}, {0, 0}, {0, 1} };
GLYPHMETRICS gm;
DWORD len = GetGlyphOutlineW(dc.GetDC(), char16_t('x'), GGO_METRICS, &gm, 0, nullptr, &kIdentityMatrix);
if (len == GDI_ERROR || gm.gmptGlyphOrigin.y <= 0) {
// 56% of ascent, best guess for true type
mMetrics->xHeight =
ROUND((double)metrics.tmAscent * DEFAULT_XHEIGHT_FACTOR);
} else {
mMetrics->xHeight = gm.gmptGlyphOrigin.y;
}
len = GetGlyphOutlineW(dc.GetDC(), char16_t('H'), GGO_METRICS, &gm, 0, nullptr, &kIdentityMatrix);
if (len == GDI_ERROR || gm.gmptGlyphOrigin.y <= 0) {
mMetrics->capHeight = metrics.tmAscent - metrics.tmInternalLeading;
} else {
mMetrics->capHeight = gm.gmptGlyphOrigin.y;
}
mMetrics->emHeight = metrics.tmHeight - metrics.tmInternalLeading;
gfxFloat typEmHeight = (double)oMetrics.otmAscent - (double)oMetrics.otmDescent;
mMetrics->emAscent = ROUND(mMetrics->emHeight * (double)oMetrics.otmAscent / typEmHeight);
mMetrics->emDescent = mMetrics->emHeight - mMetrics->emAscent;
if (oMetrics.otmEMSquare > 0) {
mFUnitsConvFactor = float(mAdjustedSize / oMetrics.otmEMSquare);
}
} else {
// Make a best-effort guess at extended metrics
// this is based on general typographic guidelines
// GetTextMetrics can fail if the font file has been removed
// or corrupted recently.
BOOL result = GetTextMetrics(dc.GetDC(), &metrics);
if (!result) {
NS_WARNING("Missing or corrupt font data, fasten your seatbelt");
mIsValid = false;
memset(mMetrics, 0, sizeof(*mMetrics));
return;
}
mMetrics->xHeight =
ROUND((float)metrics.tmAscent * DEFAULT_XHEIGHT_FACTOR);
mMetrics->strikeoutSize = 1;
mMetrics->strikeoutOffset = ROUND(mMetrics->xHeight * 0.5f); // 50% of xHeight
mMetrics->underlineSize = 1;
mMetrics->underlineOffset = -ROUND((float)metrics.tmDescent * 0.30f); // 30% of descent
mMetrics->emHeight = metrics.tmHeight - metrics.tmInternalLeading;
mMetrics->emAscent = metrics.tmAscent - metrics.tmInternalLeading;
mMetrics->emDescent = metrics.tmDescent;
mMetrics->capHeight = mMetrics->emAscent;
}
mMetrics->internalLeading = metrics.tmInternalLeading;
mMetrics->externalLeading = metrics.tmExternalLeading;
mMetrics->maxHeight = metrics.tmHeight;
mMetrics->maxAscent = metrics.tmAscent;
mMetrics->maxDescent = metrics.tmDescent;
mMetrics->maxAdvance = metrics.tmMaxCharWidth;
mMetrics->aveCharWidth = std::max<gfxFloat>(1, metrics.tmAveCharWidth);
// The font is monospace when TMPF_FIXED_PITCH is *not* set!
// See http://msdn2.microsoft.com/en-us/library/ms534202(VS.85).aspx
if (!(metrics.tmPitchAndFamily & TMPF_FIXED_PITCH)) {
mMetrics->maxAdvance = mMetrics->aveCharWidth;
}
// For fonts with USE_TYPO_METRICS set in the fsSelection field,
// let the OS/2 sTypo* metrics override the previous values.
// (see http://www.microsoft.com/typography/otspec/os2.htm#fss)
// Using the equivalent values from oMetrics provides inconsistent
// results with CFF fonts, so we instead rely on OS2Table.
gfxFontEntry::AutoTable os2Table(mFontEntry,
TRUETYPE_TAG('O','S','/','2'));
if (os2Table) {
uint32_t len;
const OS2Table *os2 =
reinterpret_cast<const OS2Table*>(hb_blob_get_data(os2Table,
&len));
if (len >= offsetof(OS2Table, sTypoLineGap) + sizeof(int16_t)) {
const uint16_t kUseTypoMetricsMask = 1 << 7;
if ((uint16_t(os2->fsSelection) & kUseTypoMetricsMask)) {
double ascent = int16_t(os2->sTypoAscender);
double descent = int16_t(os2->sTypoDescender);
double lineGap = int16_t(os2->sTypoLineGap);
mMetrics->maxAscent = ROUND(ascent * mFUnitsConvFactor);
mMetrics->maxDescent = -ROUND(descent * mFUnitsConvFactor);
mMetrics->maxHeight =
mMetrics->maxAscent + mMetrics->maxDescent;
mMetrics->internalLeading =
mMetrics->maxHeight - mMetrics->emHeight;
gfxFloat lineHeight =
ROUND((ascent - descent + lineGap) * mFUnitsConvFactor);
lineHeight = std::max(lineHeight, mMetrics->maxHeight);
mMetrics->externalLeading =
lineHeight - mMetrics->maxHeight;
}
}
// although sxHeight and sCapHeight are signed fields, we consider
// negative values to be erroneous and just ignore them
if (uint16_t(os2->version) >= 2) {
// version 2 and later includes the x-height and cap-height fields
if (len >= offsetof(OS2Table, sxHeight) + sizeof(int16_t) &&
int16_t(os2->sxHeight) > 0) {
mMetrics->xHeight = ROUND(int16_t(os2->sxHeight) * mFUnitsConvFactor);
}
if (len >= offsetof(OS2Table, sCapHeight) + sizeof(int16_t) &&
int16_t(os2->sCapHeight) > 0) {
mMetrics->capHeight = ROUND(int16_t(os2->sCapHeight) * mFUnitsConvFactor);
}
}
}
WORD glyph;
SIZE size;
DWORD ret = GetGlyphIndicesW(dc.GetDC(), L" ", 1, &glyph,
GGI_MARK_NONEXISTING_GLYPHS);
if (ret != GDI_ERROR && glyph != 0xFFFF) {
mSpaceGlyph = glyph;
// Cache the width of a single space.
GetTextExtentPoint32W(dc.GetDC(), L" ", 1, &size);
mMetrics->spaceWidth = ROUND(size.cx);
} else {
mMetrics->spaceWidth = mMetrics->aveCharWidth;
}
// Cache the width of digit zero, if available.
ret = GetGlyphIndicesW(dc.GetDC(), L"0", 1, &glyph,
GGI_MARK_NONEXISTING_GLYPHS);
if (ret != GDI_ERROR && glyph != 0xFFFF) {
GetTextExtentPoint32W(dc.GetDC(), L"0", 1, &size);
mMetrics->zeroOrAveCharWidth = ROUND(size.cx);
} else {
mMetrics->zeroOrAveCharWidth = mMetrics->aveCharWidth;
}
SanitizeMetrics(mMetrics, GetFontEntry()->mIsBadUnderlineFont);
} else {
mFUnitsConvFactor = 0.0; // zero-sized font: all values scale to zero
}
if (IsSyntheticBold()) {
mMetrics->aveCharWidth += GetSyntheticBoldOffset();
mMetrics->maxAdvance += GetSyntheticBoldOffset();
}
mFontFace = cairo_win32_font_face_create_for_logfontw_hfont(&logFont,
mFont);
cairo_matrix_t sizeMatrix, ctm;
cairo_matrix_init_identity(&ctm);
cairo_matrix_init_scale(&sizeMatrix, mAdjustedSize, mAdjustedSize);
if (useCairoFakeItalic) {
// Skew the matrix to do fake italic if it wasn't already applied
// via the LOGFONT
double skewfactor = OBLIQUE_SKEW_FACTOR;
cairo_matrix_t style;
cairo_matrix_init(&style,
1, //xx
0, //yx
-1 * skewfactor, //xy
1, //yy
0, //x0
0); //y0
cairo_matrix_multiply(&sizeMatrix, &sizeMatrix, &style);
}
cairo_font_options_t *fontOptions = cairo_font_options_create();
if (mAntialiasOption != kAntialiasDefault) {
cairo_font_options_set_antialias(fontOptions,
GetCairoAntialiasOption(mAntialiasOption));
}
mScaledFont = cairo_scaled_font_create(mFontFace, &sizeMatrix,
&ctm, fontOptions);
cairo_font_options_destroy(fontOptions);
if (!mScaledFont ||
cairo_scaled_font_status(mScaledFont) != CAIRO_STATUS_SUCCESS) {
#ifdef DEBUG
char warnBuf[1024];
SprintfLiteral(warnBuf, "Failed to create scaled font: %s status: %d",
NS_ConvertUTF16toUTF8(mFontEntry->Name()).get(),
mScaledFont ? cairo_scaled_font_status(mScaledFont) : 0);
NS_WARNING(warnBuf);
#endif
mIsValid = false;
} else {
mIsValid = true;
}
#if 0
printf("Font: %p (%s) size: %f adjusted size: %f valid: %s\n", this,
NS_ConvertUTF16toUTF8(GetName()).get(), mStyle.size, mAdjustedSize, (mIsValid ? "yes" : "no"));
printf(" emHeight: %f emAscent: %f emDescent: %f\n", mMetrics->emHeight, mMetrics->emAscent, mMetrics->emDescent);
printf(" maxAscent: %f maxDescent: %f maxAdvance: %f\n", mMetrics->maxAscent, mMetrics->maxDescent, mMetrics->maxAdvance);
printf(" internalLeading: %f externalLeading: %f\n", mMetrics->internalLeading, mMetrics->externalLeading);
printf(" spaceWidth: %f aveCharWidth: %f\n", mMetrics->spaceWidth, mMetrics->aveCharWidth);
printf(" xHeight: %f capHeight: %f\n", mMetrics->xHeight, mMetrics->capHeight);
printf(" uOff: %f uSize: %f stOff: %f stSize: %f\n",
mMetrics->underlineOffset, mMetrics->underlineSize, mMetrics->strikeoutOffset, mMetrics->strikeoutSize);
#endif
}
void
gfxGDIFont::FillLogFont(LOGFONTW& aLogFont, gfxFloat aSize,
bool aUseGDIFakeItalic)
{
GDIFontEntry *fe = static_cast<GDIFontEntry*>(GetFontEntry());
uint16_t weight;
if (fe->IsUserFont()) {
if (fe->IsLocalUserFont()) {
// for local user fonts, don't change the original weight
// in the entry's logfont, because that could alter the
// choice of actual face used (bug 724231)
weight = 0;
} else {
// avoid GDI synthetic bold which occurs when weight
// specified is >= font data weight + 200
weight = mNeedsBold ? 700 : 200;
}
} else {
weight = mNeedsBold ? 700 : fe->Weight();
}
fe->FillLogFont(&aLogFont, weight, aSize,
(mAntialiasOption == kAntialiasSubpixel) ? true : false);
// If GDI synthetic italic is wanted, force the lfItalic field to true
if (aUseGDIFakeItalic) {
aLogFont.lfItalic = 1;
}
}
uint32_t
gfxGDIFont::GetGlyph(uint32_t aUnicode, uint32_t aVarSelector)
{
// Callback used only for fonts that lack a 'cmap' table.
// We don't support variation selector sequences or non-BMP characters
// in the legacy bitmap, vector or postscript fonts that might use
// this code path.
if (aUnicode > 0xffff || aVarSelector) {
return 0;
}
if (!mGlyphIDs) {
mGlyphIDs = MakeUnique<nsDataHashtable<nsUint32HashKey,uint32_t>>(64);
}
uint32_t gid;
if (mGlyphIDs->Get(aUnicode, &gid)) {
return gid;
}
wchar_t ch = aUnicode;
WORD glyph;
DWORD ret = ScriptGetCMap(nullptr, &mScriptCache, &ch, 1, 0, &glyph);
if (ret != S_OK) {
AutoDC dc;
AutoSelectFont fs(dc.GetDC(), GetHFONT());
if (ret == E_PENDING) {
// Try ScriptGetCMap again now that we've set up the font.
ret = ScriptGetCMap(dc.GetDC(), &mScriptCache, &ch, 1, 0, &glyph);
}
if (ret != S_OK) {
// If ScriptGetCMap still failed, fall back to GetGlyphIndicesW
// (see bug 1105807).
ret = GetGlyphIndicesW(dc.GetDC(), &ch, 1, &glyph,
GGI_MARK_NONEXISTING_GLYPHS);
if (ret == GDI_ERROR || glyph == 0xFFFF) {
glyph = 0;
}
}
}
mGlyphIDs->Put(aUnicode, glyph);
return glyph;
}
int32_t
gfxGDIFont::GetGlyphWidth(DrawTarget& aDrawTarget, uint16_t aGID)
{
if (!mGlyphWidths) {
mGlyphWidths = MakeUnique<nsDataHashtable<nsUint32HashKey,int32_t>>(128);
}
int32_t width;
if (mGlyphWidths->Get(aGID, &width)) {
return width;
}
DCFromDrawTarget dc(aDrawTarget);
AutoSelectFont fs(dc, GetHFONT());
int devWidth;
if (GetCharWidthI(dc, aGID, 1, nullptr, &devWidth)) {
// clamp value to range [0..0x7fff], and convert to 16.16 fixed-point
devWidth = std::min(std::max(0, devWidth), 0x7fff);
width = devWidth << 16;
mGlyphWidths->Put(aGID, width);
return width;
}
return -1;
}
void
gfxGDIFont::AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontCacheSizes* aSizes) const
{
gfxFont::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
aSizes->mFontInstances += aMallocSizeOf(mMetrics);
if (mGlyphWidths) {
aSizes->mFontInstances +=
mGlyphWidths->ShallowSizeOfIncludingThis(aMallocSizeOf);
}
}
void
gfxGDIFont::AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontCacheSizes* aSizes) const
{
aSizes->mFontInstances += aMallocSizeOf(this);
AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
}

108
gfx/thebes/gfxGDIFont.h Normal file
View file

@ -0,0 +1,108 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_GDIFONT_H
#define GFX_GDIFONT_H
#include "mozilla/MemoryReporting.h"
#include "gfxFont.h"
#include "gfxGDIFontList.h"
#include "nsDataHashtable.h"
#include "nsHashKeys.h"
#include "cairo.h"
#include "usp10.h"
class gfxGDIFont : public gfxFont
{
public:
gfxGDIFont(GDIFontEntry *aFontEntry,
const gfxFontStyle *aFontStyle,
bool aNeedsBold,
AntialiasOption anAAOption = kAntialiasDefault);
virtual ~gfxGDIFont();
HFONT GetHFONT() { return mFont; }
cairo_font_face_t *CairoFontFace() { return mFontFace; }
cairo_scaled_font_t *CairoScaledFont() { return mScaledFont; }
/* overrides for the pure virtual methods in gfxFont */
virtual uint32_t GetSpaceGlyph() override;
virtual bool SetupCairoFont(DrawTarget* aDrawTarget) override;
/* override Measure to add padding for antialiasing */
virtual RunMetrics Measure(const gfxTextRun *aTextRun,
uint32_t aStart, uint32_t aEnd,
BoundingBoxType aBoundingBoxType,
DrawTarget *aDrawTargetForTightBoundingBox,
Spacing *aSpacing,
uint16_t aOrientation) override;
/* required for MathML to suppress effects of ClearType "padding" */
virtual gfxFont*
CopyWithAntialiasOption(AntialiasOption anAAOption) override;
// If the font has a cmap table, we handle it purely with harfbuzz;
// but if not (e.g. .fon fonts), we'll use a GDI callback to get glyphs.
virtual bool ProvidesGetGlyph() const override {
return !mFontEntry->HasCmapTable();
}
virtual uint32_t GetGlyph(uint32_t aUnicode,
uint32_t aVarSelector) override;
virtual bool ProvidesGlyphWidths() const override { return true; }
// get hinted glyph width in pixels as 16.16 fixed-point value
virtual int32_t GetGlyphWidth(DrawTarget& aDrawTarget,
uint16_t aGID) override;
virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontCacheSizes* aSizes) const;
virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontCacheSizes* aSizes) const;
virtual FontType GetType() const override { return FONT_TYPE_GDI; }
protected:
virtual const Metrics& GetHorizontalMetrics() override;
/* override to ensure the cairo font is set up properly */
virtual bool ShapeText(DrawTarget *aDrawTarget,
const char16_t *aText,
uint32_t aOffset,
uint32_t aLength,
Script aScript,
bool aVertical,
gfxShapedText *aShapedText) override;
void Initialize(); // creates metrics and Cairo fonts
// Fill the given LOGFONT record according to our style, but don't adjust
// the lfItalic field if we're going to use a cairo transform for fake
// italics.
void FillLogFont(LOGFONTW& aLogFont, gfxFloat aSize, bool aUseGDIFakeItalic);
HFONT mFont;
cairo_font_face_t *mFontFace;
Metrics *mMetrics;
uint32_t mSpaceGlyph;
bool mNeedsBold;
// cache of glyph IDs (used for non-sfnt fonts only)
mozilla::UniquePtr<nsDataHashtable<nsUint32HashKey,uint32_t> > mGlyphIDs;
SCRIPT_CACHE mScriptCache;
// cache of glyph widths in 16.16 fixed-point pixels
mozilla::UniquePtr<nsDataHashtable<nsUint32HashKey,int32_t> > mGlyphWidths;
};
#endif /* GFX_GDIFONT_H */

File diff suppressed because it is too large Load diff

354
gfx/thebes/gfxGDIFontList.h Normal file
View file

@ -0,0 +1,354 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_GDIFONTLIST_H
#define GFX_GDIFONTLIST_H
#include "mozilla/MemoryReporting.h"
#include "gfxWindowsPlatform.h"
#include "gfxPlatformFontList.h"
#include "nsGkAtoms.h"
#include <windows.h>
class AutoDC // get the global device context, and auto-release it on destruction
{
public:
AutoDC() {
mDC = ::GetDC(nullptr);
}
~AutoDC() {
::ReleaseDC(nullptr, mDC);
}
HDC GetDC() {
return mDC;
}
private:
HDC mDC;
};
class AutoSelectFont // select a font into the given DC, and auto-restore
{
public:
AutoSelectFont(HDC aDC, LOGFONTW *aLogFont)
: mOwnsFont(false)
{
mFont = ::CreateFontIndirectW(aLogFont);
if (mFont) {
mOwnsFont = true;
mDC = aDC;
mOldFont = (HFONT)::SelectObject(aDC, mFont);
} else {
mOldFont = nullptr;
}
}
AutoSelectFont(HDC aDC, HFONT aFont)
: mOwnsFont(false)
{
mDC = aDC;
mFont = aFont;
mOldFont = (HFONT)::SelectObject(aDC, aFont);
}
~AutoSelectFont() {
if (mOldFont) {
::SelectObject(mDC, mOldFont);
if (mOwnsFont) {
::DeleteObject(mFont);
}
}
}
bool IsValid() const {
return mFont != nullptr;
}
HFONT GetFont() const {
return mFont;
}
private:
HDC mDC;
HFONT mFont;
HFONT mOldFont;
bool mOwnsFont;
};
/**
* List of different types of fonts we support on Windows.
* These can generally be lumped in to 3 categories where we have to
* do special things: Really old fonts bitmap and vector fonts (device
* and raster), Type 1 fonts, and TrueType/OpenType fonts.
*
* This list is sorted in order from least prefered to most prefered.
* We prefer Type1 fonts over OpenType fonts to avoid falling back to
* things like Arial (opentype) when you ask for Helvetica (type1)
**/
enum gfxWindowsFontType {
GFX_FONT_TYPE_UNKNOWN = 0,
GFX_FONT_TYPE_DEVICE,
GFX_FONT_TYPE_RASTER,
GFX_FONT_TYPE_TRUETYPE,
GFX_FONT_TYPE_PS_OPENTYPE,
GFX_FONT_TYPE_TT_OPENTYPE,
GFX_FONT_TYPE_TYPE1
};
// A single member of a font family (i.e. a single face, such as Times Italic)
// represented as a LOGFONT that will resolve to the correct face.
// This replaces FontEntry from gfxWindowsFonts.h/cpp.
class GDIFontEntry : public gfxFontEntry
{
public:
LPLOGFONTW GetLogFont() { return &mLogFont; }
nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr);
virtual bool IsSymbolFont();
void FillLogFont(LOGFONTW *aLogFont, uint16_t aWeight, gfxFloat aSize,
bool aUseCleartype);
static gfxWindowsFontType DetermineFontType(const NEWTEXTMETRICW& metrics,
DWORD fontType)
{
gfxWindowsFontType feType;
if (metrics.ntmFlags & NTM_TYPE1)
feType = GFX_FONT_TYPE_TYPE1;
else if (metrics.ntmFlags & NTM_PS_OPENTYPE)
feType = GFX_FONT_TYPE_PS_OPENTYPE;
else if (metrics.ntmFlags & NTM_TT_OPENTYPE)
feType = GFX_FONT_TYPE_TT_OPENTYPE;
else if (fontType == TRUETYPE_FONTTYPE)
feType = GFX_FONT_TYPE_TRUETYPE;
else if (fontType == RASTER_FONTTYPE)
feType = GFX_FONT_TYPE_RASTER;
else if (fontType == DEVICE_FONTTYPE)
feType = GFX_FONT_TYPE_DEVICE;
else
feType = GFX_FONT_TYPE_UNKNOWN;
return feType;
}
bool IsType1() const {
return (mFontType == GFX_FONT_TYPE_TYPE1);
}
bool IsTrueType() const {
return (mFontType == GFX_FONT_TYPE_TRUETYPE ||
mFontType == GFX_FONT_TYPE_PS_OPENTYPE ||
mFontType == GFX_FONT_TYPE_TT_OPENTYPE);
}
virtual bool MatchesGenericFamily(const nsACString& aGeneric) const {
if (aGeneric.IsEmpty()) {
return true;
}
// Japanese 'Mincho' fonts do not belong to FF_MODERN even if
// they are fixed pitch because they have variable stroke width.
if (mWindowsFamily == FF_ROMAN && mWindowsPitch & FIXED_PITCH) {
return aGeneric.EqualsLiteral("monospace");
}
// Japanese 'Gothic' fonts do not belong to FF_SWISS even if
// they are variable pitch because they have constant stroke width.
if (mWindowsFamily == FF_MODERN && mWindowsPitch & VARIABLE_PITCH) {
return aGeneric.EqualsLiteral("sans-serif");
}
// All other fonts will be grouped correctly using family...
switch (mWindowsFamily) {
case FF_DONTCARE:
return false;
case FF_ROMAN:
return aGeneric.EqualsLiteral("serif");
case FF_SWISS:
return aGeneric.EqualsLiteral("sans-serif");
case FF_MODERN:
return aGeneric.EqualsLiteral("monospace");
case FF_SCRIPT:
return aGeneric.EqualsLiteral("cursive");
case FF_DECORATIVE:
return aGeneric.EqualsLiteral("fantasy");
}
return false;
}
virtual bool SupportsLangGroup(nsIAtom* aLangGroup) const override {
if (!aLangGroup || aLangGroup == nsGkAtoms::Unicode) {
return true;
}
int16_t bit = -1;
/* map our langgroup names in to Windows charset bits */
if (aLangGroup == nsGkAtoms::x_western) {
bit = ANSI_CHARSET;
} else if (aLangGroup == nsGkAtoms::Japanese) {
bit = SHIFTJIS_CHARSET;
} else if (aLangGroup == nsGkAtoms::ko) {
bit = HANGEUL_CHARSET;
} else if (aLangGroup == nsGkAtoms::zh_cn) {
bit = GB2312_CHARSET;
} else if (aLangGroup == nsGkAtoms::zh_tw) {
bit = CHINESEBIG5_CHARSET;
} else if (aLangGroup == nsGkAtoms::el_) {
bit = GREEK_CHARSET;
} else if (aLangGroup == nsGkAtoms::he) {
bit = HEBREW_CHARSET;
} else if (aLangGroup == nsGkAtoms::ar) {
bit = ARABIC_CHARSET;
} else if (aLangGroup == nsGkAtoms::x_cyrillic) {
bit = RUSSIAN_CHARSET;
} else if (aLangGroup == nsGkAtoms::th) {
bit = THAI_CHARSET;
}
if (bit != -1) {
return mCharset.test(bit);
}
return false;
}
virtual bool SupportsRange(uint8_t range) {
return mUnicodeRanges.test(range);
}
virtual bool SkipDuringSystemFallback() {
return !HasCmapTable(); // explicitly skip non-SFNT fonts
}
virtual bool TestCharacterMap(uint32_t aCh);
virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontListSizes* aSizes) const;
// create a font entry for a font with a given name
static GDIFontEntry* CreateFontEntry(const nsAString& aName,
gfxWindowsFontType aFontType,
uint8_t aStyle,
uint16_t aWeight, int16_t aStretch,
gfxUserFontData* aUserFontData,
bool aFamilyHasItalicFace);
// create a font entry for a font referenced by its fullname
static GDIFontEntry* LoadLocalFont(const nsAString& aFontName,
uint16_t aWeight,
int16_t aStretch,
uint8_t aStyle);
uint8_t mWindowsFamily;
uint8_t mWindowsPitch;
gfxWindowsFontType mFontType;
bool mForceGDI : 1;
// For src:local user-fonts, we keep track of whether the platform family
// contains an italic face, because in this case we can't safely ask GDI
// to create synthetic italics (oblique) via the LOGFONT.
// (For other types of font, this is just set to false.)
bool mFamilyHasItalicFace : 1;
gfxSparseBitSet mCharset;
gfxSparseBitSet mUnicodeRanges;
protected:
friend class gfxWindowsFont;
GDIFontEntry(const nsAString& aFaceName, gfxWindowsFontType aFontType,
uint8_t aStyle, uint16_t aWeight, int16_t aStretch,
gfxUserFontData *aUserFontData, bool aFamilyHasItalicFace);
void InitLogFont(const nsAString& aName, gfxWindowsFontType aFontType);
virtual gfxFont *CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold);
virtual nsresult CopyFontTable(uint32_t aTableTag,
nsTArray<uint8_t>& aBuffer) override;
LOGFONTW mLogFont;
};
// a single font family, referencing one or more faces
class GDIFontFamily : public gfxFontFamily
{
public:
GDIFontFamily(nsAString &aName) :
gfxFontFamily(aName) {}
virtual void FindStyleVariations(FontInfoData *aFontInfoData = nullptr);
private:
static int CALLBACK FamilyAddStylesProc(const ENUMLOGFONTEXW *lpelfe,
const NEWTEXTMETRICEXW *nmetrics,
DWORD fontType, LPARAM data);
};
class gfxGDIFontList : public gfxPlatformFontList {
public:
static gfxGDIFontList* PlatformFontList() {
return static_cast<gfxGDIFontList*>(sPlatformFontList);
}
// initialize font lists
virtual nsresult InitFontListForPlatform() override;
bool FindAndAddFamilies(const nsAString& aFamily,
nsTArray<gfxFontFamily*>* aOutput,
gfxFontStyle* aStyle = nullptr,
gfxFloat aDevToCssSize = 1.0) override;
virtual gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
uint16_t aWeight,
int16_t aStretch,
uint8_t aStyle);
virtual gfxFontEntry* MakePlatformFont(const nsAString& aFontName,
uint16_t aWeight,
int16_t aStretch,
uint8_t aStyle,
const uint8_t* aFontData,
uint32_t aLength);
virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontListSizes* aSizes) const;
virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontListSizes* aSizes) const;
protected:
virtual gfxFontFamily*
GetDefaultFontForPlatform(const gfxFontStyle* aStyle) override;
private:
friend class gfxWindowsPlatform;
gfxGDIFontList();
nsresult GetFontSubstitutes();
static int CALLBACK EnumFontFamExProc(ENUMLOGFONTEXW *lpelfe,
NEWTEXTMETRICEXW *lpntme,
DWORD fontType,
LPARAM lParam);
virtual already_AddRefed<FontInfoData> CreateFontInfoData();
#ifdef MOZ_BUNDLED_FONTS
void ActivateBundledFonts();
#endif
FontFamilyTable mFontSubstitutes;
nsTArray<nsString> mNonExistingFonts;
};
#endif /* GFX_GDIFONTLIST_H */

View file

@ -0,0 +1,69 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gfxGdkNativeRenderer.h"
#include "gfxContext.h"
#include "gfxPlatformGtk.h"
#ifdef MOZ_X11
#include <gdk/gdkx.h>
#include "cairo-xlib.h"
#include "gfxXlibSurface.h"
#if (MOZ_WIDGET_GTK == 2)
nsresult
gfxGdkNativeRenderer::DrawWithXlib(cairo_surface_t* surface,
nsIntPoint offset,
mozilla::gfx::IntRect* clipRects, uint32_t numClipRects)
{
GdkDrawable *drawable = gfxPlatformGtk::GetGdkDrawable(surface);
if (!drawable) {
int depth = cairo_xlib_surface_get_depth(surface);
GdkScreen* screen = gdk_colormap_get_screen(mColormap);
drawable =
gdk_pixmap_foreign_new_for_screen(screen, cairo_xlib_surface_get_drawable(surface),
cairo_xlib_surface_get_width(surface),
cairo_xlib_surface_get_height(surface),
depth);
if (!drawable)
return NS_ERROR_FAILURE;
gdk_drawable_set_colormap(drawable, mColormap);
gfxPlatformGtk::SetGdkDrawable(surface, drawable);
g_object_unref(drawable); // The drawable now belongs to |surface|.
}
GdkRectangle clipRect;
if (numClipRects) {
NS_ASSERTION(numClipRects == 1, "Too many clip rects");
clipRect.x = clipRects[0].x;
clipRect.y = clipRects[0].y;
clipRect.width = clipRects[0].width;
clipRect.height = clipRects[0].height;
}
return DrawWithGDK(drawable, offset.x, offset.y,
numClipRects ? &clipRect : nullptr, numClipRects);
}
void
gfxGdkNativeRenderer::Draw(gfxContext* ctx, mozilla::gfx::IntSize size,
uint32_t flags, GdkColormap* colormap)
{
mColormap = colormap;
Visual* visual =
gdk_x11_visual_get_xvisual(gdk_colormap_get_visual(colormap));
Screen* screen =
gdk_x11_screen_get_xscreen(gdk_colormap_get_screen(colormap));
gfxXlibNativeRenderer::Draw(ctx, size, flags, screen, visual);
}
#else
// TODO GTK3
#endif
#endif

View file

@ -0,0 +1,88 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFXGDKNATIVERENDER_H_
#define GFXGDKNATIVERENDER_H_
#include <gdk/gdk.h>
#include "nsSize.h"
#ifdef MOZ_X11
#include "gfxXlibNativeRenderer.h"
#endif
class gfxContext;
/**
* This class lets us take code that draws into an GDK drawable and lets us
* use it to draw into any Thebes context. The user should subclass this class,
* override DrawWithGDK, and then call Draw(). The drawing will be subjected
* to all Thebes transformations, clipping etc.
*/
class gfxGdkNativeRenderer
#ifdef MOZ_X11
: private gfxXlibNativeRenderer
#endif
{
public:
/**
* Perform the native drawing.
* @param offsetX draw at this offset into the given drawable
* @param offsetY draw at this offset into the given drawable
* @param clipRects an array of rects; clip to the union
* @param numClipRects the number of rects in the array, or zero if
* no clipping is required
*/
#if (MOZ_WIDGET_GTK == 2)
virtual nsresult DrawWithGDK(GdkDrawable * drawable, gint offsetX,
gint offsetY, GdkRectangle * clipRects, uint32_t numClipRects) = 0;
#endif
enum {
// If set, then Draw() is opaque, i.e., every pixel in the intersection
// of the clipRect and (offset.x,offset.y,bounds.width,bounds.height)
// will be set and there is no dependence on what the existing pixels
// in the drawable are set to.
DRAW_IS_OPAQUE =
#ifdef MOZ_X11
gfxXlibNativeRenderer::DRAW_IS_OPAQUE
#else
0x1
#endif
// If set, then numClipRects can be zero or one.
// If not set, then numClipRects will be zero.
, DRAW_SUPPORTS_CLIP_RECT =
#ifdef MOZ_X11
gfxXlibNativeRenderer::DRAW_SUPPORTS_CLIP_RECT
#else
0x2
#endif
};
/**
* @param flags see above
* @param bounds Draw()'s drawing is guaranteed to be restricted to
* the rectangle (offset.x,offset.y,bounds.width,bounds.height)
* @param dpy a display to use for the drawing if ctx doesn't have one
*/
#if (MOZ_WIDGET_GTK == 2)
void Draw(gfxContext* ctx, mozilla::gfx::IntSize size,
uint32_t flags, GdkColormap* colormap);
#endif
private:
#ifdef MOZ_X11
// for gfxXlibNativeRenderer:
virtual nsresult DrawWithXlib(cairo_surface_t* surface,
nsIntPoint offset,
mozilla::gfx::IntRect* clipRects, uint32_t numClipRects) override;
#if (MOZ_WIDGET_GTK == 2)
GdkColormap *mColormap;
#endif
#endif
};
#endif /*GFXGDKNATIVERENDER_H_*/

View file

@ -0,0 +1,154 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gfxGlyphExtents.h"
#include "gfxTextRun.h"
using namespace mozilla;
#ifdef DEBUG_roc
#define DEBUG_TEXT_RUN_STORAGE_METRICS
#endif
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
extern uint32_t gTextRunStorageHighWaterMark;
extern uint32_t gTextRunStorage;
extern uint32_t gFontCount;
extern uint32_t gGlyphExtentsCount;
extern uint32_t gGlyphExtentsWidthsTotalSize;
extern uint32_t gGlyphExtentsSetupEagerSimple;
extern uint32_t gGlyphExtentsSetupEagerTight;
extern uint32_t gGlyphExtentsSetupLazyTight;
extern uint32_t gGlyphExtentsSetupFallBackToTight;
#endif
gfxGlyphExtents::~gfxGlyphExtents()
{
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
gGlyphExtentsWidthsTotalSize +=
mContainedGlyphWidths.SizeOfExcludingThis(&FontCacheMallocSizeOf);
gGlyphExtentsCount++;
#endif
MOZ_COUNT_DTOR(gfxGlyphExtents);
}
bool
gfxGlyphExtents::GetTightGlyphExtentsAppUnits(gfxFont* aFont,
DrawTarget* aDrawTarget, uint32_t aGlyphID, gfxRect* aExtents)
{
HashEntry *entry = mTightGlyphExtents.GetEntry(aGlyphID);
if (!entry) {
// Some functions higher up in the call chain deliberately pass in a
// nullptr DrawTarget, e.g. GetBaselinePosition() passes nullptr to
// gfxTextRun::MeasureText() and that nullptr reaches here.
if (!aDrawTarget) {
NS_WARNING("Could not get glyph extents (no aDrawTarget)");
return false;
}
if (aFont->SetupCairoFont(aDrawTarget)) {
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
++gGlyphExtentsSetupLazyTight;
#endif
aFont->SetupGlyphExtents(aDrawTarget, aGlyphID, true, this);
entry = mTightGlyphExtents.GetEntry(aGlyphID);
}
if (!entry) {
NS_WARNING("Could not get glyph extents");
return false;
}
}
*aExtents = gfxRect(entry->x, entry->y, entry->width, entry->height);
return true;
}
gfxGlyphExtents::GlyphWidths::~GlyphWidths()
{
uint32_t i, count = mBlocks.Length();
for (i = 0; i < count; ++i) {
uintptr_t bits = mBlocks[i];
if (bits && !(bits & 0x1)) {
delete[] reinterpret_cast<uint16_t *>(bits);
}
}
}
uint32_t
gfxGlyphExtents::GlyphWidths::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
{
uint32_t i;
uint32_t size = mBlocks.ShallowSizeOfExcludingThis(aMallocSizeOf);
for (i = 0; i < mBlocks.Length(); ++i) {
uintptr_t bits = mBlocks[i];
if (bits && !(bits & 0x1)) {
size += aMallocSizeOf(reinterpret_cast<void*>(bits));
}
}
return size;
}
void
gfxGlyphExtents::GlyphWidths::Set(uint32_t aGlyphID, uint16_t aWidth)
{
uint32_t block = aGlyphID >> BLOCK_SIZE_BITS;
uint32_t len = mBlocks.Length();
if (block >= len) {
uintptr_t *elems = mBlocks.AppendElements(block + 1 - len);
if (!elems)
return;
memset(elems, 0, sizeof(uintptr_t)*(block + 1 - len));
}
uintptr_t bits = mBlocks[block];
uint32_t glyphOffset = aGlyphID & (BLOCK_SIZE - 1);
if (!bits) {
mBlocks[block] = MakeSingle(glyphOffset, aWidth);
return;
}
uint16_t *newBlock;
if (bits & 0x1) {
// Expand the block to a real block. We could avoid this by checking
// glyphOffset == GetGlyphOffset(bits), but that never happens so don't bother
newBlock = new uint16_t[BLOCK_SIZE];
if (!newBlock)
return;
uint32_t i;
for (i = 0; i < BLOCK_SIZE; ++i) {
newBlock[i] = INVALID_WIDTH;
}
newBlock[GetGlyphOffset(bits)] = GetWidth(bits);
mBlocks[block] = reinterpret_cast<uintptr_t>(newBlock);
} else {
newBlock = reinterpret_cast<uint16_t *>(bits);
}
newBlock[glyphOffset] = aWidth;
}
void
gfxGlyphExtents::SetTightGlyphExtents(uint32_t aGlyphID, const gfxRect& aExtentsAppUnits)
{
HashEntry *entry = mTightGlyphExtents.PutEntry(aGlyphID);
if (!entry)
return;
entry->x = aExtentsAppUnits.X();
entry->y = aExtentsAppUnits.Y();
entry->width = aExtentsAppUnits.Width();
entry->height = aExtentsAppUnits.Height();
}
size_t
gfxGlyphExtents::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
{
return mContainedGlyphWidths.SizeOfExcludingThis(aMallocSizeOf) +
mTightGlyphExtents.ShallowSizeOfExcludingThis(aMallocSizeOf);
}
size_t
gfxGlyphExtents::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
{
return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
}

View file

@ -0,0 +1,151 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_GLYPHEXTENTS_H
#define GFX_GLYPHEXTENTS_H
#include "gfxFont.h"
#include "nsTHashtable.h"
#include "nsHashKeys.h"
#include "nsTArray.h"
#include "mozilla/MemoryReporting.h"
class gfxContext;
struct gfxRect;
namespace mozilla {
namespace gfx {
class DrawTarget;
} // namespace gfx
} // namespace mozilla
/**
* This stores glyph bounds information for a particular gfxFont, at
* a particular appunits-per-dev-pixel ratio (because the compressed glyph
* width array is stored in appunits).
*
* We store a hashtable from glyph IDs to float bounding rects. For the
* common case where the glyph has no horizontal left bearing, and no
* y overflow above the font ascent or below the font descent, and tight
* bounding boxes are not required, we avoid storing the glyph ID in the hashtable
* and instead consult an array of 16-bit glyph XMost values (in appunits).
* This array always has an entry for the font's space glyph --- the width is
* assumed to be zero.
*/
class gfxGlyphExtents {
typedef mozilla::gfx::DrawTarget DrawTarget;
public:
explicit gfxGlyphExtents(int32_t aAppUnitsPerDevUnit) :
mAppUnitsPerDevUnit(aAppUnitsPerDevUnit) {
MOZ_COUNT_CTOR(gfxGlyphExtents);
}
~gfxGlyphExtents();
enum { INVALID_WIDTH = 0xFFFF };
void NotifyGlyphsChanged() {
mTightGlyphExtents.Clear();
}
// returns INVALID_WIDTH => not a contained glyph
// Otherwise the glyph has no before-bearing or vertical bearings,
// and the result is its width measured from the baseline origin, in
// appunits.
uint16_t GetContainedGlyphWidthAppUnits(uint32_t aGlyphID) const {
return mContainedGlyphWidths.Get(aGlyphID);
}
bool IsGlyphKnown(uint32_t aGlyphID) const {
return mContainedGlyphWidths.Get(aGlyphID) != INVALID_WIDTH ||
mTightGlyphExtents.GetEntry(aGlyphID) != nullptr;
}
bool IsGlyphKnownWithTightExtents(uint32_t aGlyphID) const {
return mTightGlyphExtents.GetEntry(aGlyphID) != nullptr;
}
// Get glyph extents; a rectangle relative to the left baseline origin
// Returns true on success. Can fail on OOM or when aContext is null
// and extents were not (successfully) prefetched.
bool GetTightGlyphExtentsAppUnits(gfxFont* aFont,
DrawTarget* aDrawTarget, uint32_t aGlyphID, gfxRect* aExtents);
void SetContainedGlyphWidthAppUnits(uint32_t aGlyphID, uint16_t aWidth) {
mContainedGlyphWidths.Set(aGlyphID, aWidth);
}
void SetTightGlyphExtents(uint32_t aGlyphID, const gfxRect& aExtentsAppUnits);
int32_t GetAppUnitsPerDevUnit() { return mAppUnitsPerDevUnit; }
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
private:
class HashEntry : public nsUint32HashKey {
public:
// When constructing a new entry in the hashtable, we'll leave this
// blank. The caller of Put() will fill this in.
explicit HashEntry(KeyTypePointer aPtr) : nsUint32HashKey(aPtr) {}
HashEntry(const HashEntry& toCopy) : nsUint32HashKey(toCopy) {
x = toCopy.x; y = toCopy.y; width = toCopy.width; height = toCopy.height;
}
float x, y, width, height;
};
enum { BLOCK_SIZE_BITS = 7, BLOCK_SIZE = 1 << BLOCK_SIZE_BITS }; // 128-glyph blocks
class GlyphWidths {
public:
void Set(uint32_t aIndex, uint16_t aValue);
uint16_t Get(uint32_t aIndex) const {
uint32_t block = aIndex >> BLOCK_SIZE_BITS;
if (block >= mBlocks.Length())
return INVALID_WIDTH;
uintptr_t bits = mBlocks[block];
if (!bits)
return INVALID_WIDTH;
uint32_t indexInBlock = aIndex & (BLOCK_SIZE - 1);
if (bits & 0x1) {
if (GetGlyphOffset(bits) != indexInBlock)
return INVALID_WIDTH;
return GetWidth(bits);
}
uint16_t *widths = reinterpret_cast<uint16_t *>(bits);
return widths[indexInBlock];
}
uint32_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
~GlyphWidths();
private:
static uint32_t GetGlyphOffset(uintptr_t aBits) {
NS_ASSERTION(aBits & 0x1, "This is really a pointer...");
return (aBits >> 1) & ((1 << BLOCK_SIZE_BITS) - 1);
}
static uint32_t GetWidth(uintptr_t aBits) {
NS_ASSERTION(aBits & 0x1, "This is really a pointer...");
return aBits >> (1 + BLOCK_SIZE_BITS);
}
static uintptr_t MakeSingle(uint32_t aGlyphOffset, uint16_t aWidth) {
return (aWidth << (1 + BLOCK_SIZE_BITS)) + (aGlyphOffset << 1) + 1;
}
nsTArray<uintptr_t> mBlocks;
};
GlyphWidths mContainedGlyphWidths;
nsTHashtable<HashEntry> mTightGlyphExtents;
int32_t mAppUnitsPerDevUnit;
private:
// not implemented:
gfxGlyphExtents(const gfxGlyphExtents& aOther) = delete;
gfxGlyphExtents& operator=(const gfxGlyphExtents& aOther) = delete;
};
#endif

View file

@ -0,0 +1,239 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/gfx/2D.h"
#include "nsTArray.h"
#include "PLDHashTable.h"
#include "nsExpirationTracker.h"
#include "nsClassHashtable.h"
#include "mozilla/Telemetry.h"
#include "gfxGradientCache.h"
#include <time.h>
namespace mozilla {
namespace gfx {
using namespace mozilla;
struct GradientCacheKey : public PLDHashEntryHdr {
typedef const GradientCacheKey& KeyType;
typedef const GradientCacheKey* KeyTypePointer;
enum { ALLOW_MEMMOVE = true };
const nsTArray<GradientStop> mStops;
ExtendMode mExtend;
BackendType mBackendType;
GradientCacheKey(const nsTArray<GradientStop>& aStops, ExtendMode aExtend, BackendType aBackendType)
: mStops(aStops), mExtend(aExtend), mBackendType(aBackendType)
{ }
explicit GradientCacheKey(const GradientCacheKey* aOther)
: mStops(aOther->mStops), mExtend(aOther->mExtend), mBackendType(aOther->mBackendType)
{ }
union FloatUint32
{
float f;
uint32_t u;
};
static PLDHashNumber
HashKey(const KeyTypePointer aKey)
{
PLDHashNumber hash = 0;
FloatUint32 convert;
hash = AddToHash(hash, int(aKey->mBackendType));
hash = AddToHash(hash, int(aKey->mExtend));
for (uint32_t i = 0; i < aKey->mStops.Length(); i++) {
hash = AddToHash(hash, aKey->mStops[i].color.ToABGR());
// Use the float bits as hash, except for the cases of 0.0 and -0.0 which both map to 0
convert.f = aKey->mStops[i].offset;
hash = AddToHash(hash, convert.f ? convert.u : 0);
}
return hash;
}
bool KeyEquals(KeyTypePointer aKey) const
{
bool sameStops = true;
if (aKey->mStops.Length() != mStops.Length()) {
sameStops = false;
} else {
for (uint32_t i = 0; i < mStops.Length(); i++) {
if (mStops[i].color.ToABGR() != aKey->mStops[i].color.ToABGR() ||
mStops[i].offset != aKey->mStops[i].offset) {
sameStops = false;
break;
}
}
}
return sameStops &&
(aKey->mBackendType == mBackendType) &&
(aKey->mExtend == mExtend);
}
static KeyTypePointer KeyToPointer(KeyType aKey)
{
return &aKey;
}
};
/**
* This class is what is cached. It need to be allocated in an object separated
* to the cache entry to be able to be tracked by the nsExpirationTracker.
* */
struct GradientCacheData {
GradientCacheData(GradientStops* aStops, const GradientCacheKey& aKey)
: mStops(aStops),
mKey(aKey)
{}
GradientCacheData(const GradientCacheData& aOther)
: mStops(aOther.mStops),
mKey(aOther.mKey)
{ }
nsExpirationState *GetExpirationState() {
return &mExpirationState;
}
nsExpirationState mExpirationState;
const RefPtr<GradientStops> mStops;
GradientCacheKey mKey;
};
/**
* This class implements a cache with no maximum size, that retains the
* gfxPatterns used to draw the gradients.
*
* The key is the nsStyleGradient that defines the gradient, and the size of the
* gradient.
*
* The value is the gfxPattern, and whether or not we perform an optimization
* based on the actual gradient property.
*
* An entry stays in the cache as long as it is used often. As long as a cache
* entry is in the cache, all the references it has are guaranteed to be valid:
* the nsStyleRect for the key, the gfxPattern for the value.
*/
class GradientCache final : public nsExpirationTracker<GradientCacheData,4>
{
public:
GradientCache()
: nsExpirationTracker<GradientCacheData,4>(MAX_GENERATION_MS,
"GradientCache")
{
srand(time(nullptr));
mTimerPeriod = rand() % MAX_GENERATION_MS + 1;
Telemetry::Accumulate(Telemetry::GRADIENT_RETENTION_TIME, mTimerPeriod);
}
virtual void NotifyExpired(GradientCacheData* aObject)
{
// This will free the gfxPattern.
RemoveObject(aObject);
mHashEntries.Remove(aObject->mKey);
}
GradientCacheData* Lookup(const nsTArray<GradientStop>& aStops, ExtendMode aExtend, BackendType aBackendType)
{
GradientCacheData* gradient =
mHashEntries.Get(GradientCacheKey(aStops, aExtend, aBackendType));
if (gradient) {
MarkUsed(gradient);
}
return gradient;
}
// Returns true if we successfully register the gradient in the cache, false
// otherwise.
bool RegisterEntry(GradientCacheData* aValue)
{
nsresult rv = AddObject(aValue);
if (NS_FAILED(rv)) {
// We are OOM, and we cannot track this object. We don't want stall
// entries in the hash table (since the expiration tracker is responsible
// for removing the cache entries), so we avoid putting that entry in the
// table, which is a good things considering we are short on memory
// anyway, we probably don't want to retain things.
return false;
}
mHashEntries.Put(aValue->mKey, aValue);
return true;
}
protected:
uint32_t mTimerPeriod;
static const uint32_t MAX_GENERATION_MS = 10000;
/**
* FIXME use nsTHashtable to avoid duplicating the GradientCacheKey.
* https://bugzilla.mozilla.org/show_bug.cgi?id=761393#c47
*/
nsClassHashtable<GradientCacheKey, GradientCacheData> mHashEntries;
};
static GradientCache* gGradientCache = nullptr;
GradientStops *
gfxGradientCache::GetGradientStops(const DrawTarget *aDT, nsTArray<GradientStop>& aStops, ExtendMode aExtend)
{
if (!gGradientCache) {
gGradientCache = new GradientCache();
}
GradientCacheData* cached =
gGradientCache->Lookup(aStops, aExtend, aDT->GetBackendType());
if (cached && cached->mStops) {
if (!cached->mStops->IsValid()) {
gGradientCache->NotifyExpired(cached);
} else {
return cached->mStops;
}
}
return nullptr;
}
already_AddRefed<GradientStops>
gfxGradientCache::GetOrCreateGradientStops(const DrawTarget *aDT, nsTArray<GradientStop>& aStops, ExtendMode aExtend)
{
if (aDT->IsRecording()) {
return aDT->CreateGradientStops(aStops.Elements(), aStops.Length(), aExtend);
}
RefPtr<GradientStops> gs = GetGradientStops(aDT, aStops, aExtend);
if (!gs) {
gs = aDT->CreateGradientStops(aStops.Elements(), aStops.Length(), aExtend);
if (!gs) {
return nullptr;
}
GradientCacheData *cached =
new GradientCacheData(gs, GradientCacheKey(aStops, aExtend,
aDT->GetBackendType()));
if (!gGradientCache->RegisterEntry(cached)) {
delete cached;
}
}
return gs.forget();
}
void
gfxGradientCache::PurgeAllCaches()
{
if (gGradientCache) {
gGradientCache->AgeAllGenerations();
}
}
void
gfxGradientCache::Shutdown()
{
delete gGradientCache;
gGradientCache = nullptr;
}
} // namespace gfx
} // namespace mozilla

View file

@ -0,0 +1,36 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_GRADIENT_CACHE_H
#define GFX_GRADIENT_CACHE_H
#include "nsTArray.h"
#include "gfxPattern.h"
#include "mozilla/gfx/2D.h"
namespace mozilla {
namespace gfx {
class gfxGradientCache {
public:
static gfx::GradientStops*
GetGradientStops(const gfx::DrawTarget *aDT,
nsTArray<gfx::GradientStop>& aStops,
gfx::ExtendMode aExtend);
static already_AddRefed<gfx::GradientStops>
GetOrCreateGradientStops(const gfx::DrawTarget *aDT,
nsTArray<gfx::GradientStop>& aStops,
gfx::ExtendMode aExtend);
static void PurgeAllCaches();
static void Shutdown();
};
} // namespace gfx
} // namespace mozilla
#endif

Some files were not shown because too many files have changed in this diff Show more