mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-05 06:47:31 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
1039
gfx/thebes/CJKCompatSVS.cpp
Normal file
1039
gfx/thebes/CJKCompatSVS.cpp
Normal file
File diff suppressed because it is too large
Load diff
137
gfx/thebes/ContextStateTracker.cpp
Normal file
137
gfx/thebes/ContextStateTracker.cpp
Normal 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
|
||||
|
||||
84
gfx/thebes/ContextStateTracker.h
Normal file
84
gfx/thebes/ContextStateTracker.h
Normal 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
412
gfx/thebes/D3D11Checks.cpp
Normal 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
30
gfx/thebes/D3D11Checks.h
Normal 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
|
||||
878
gfx/thebes/DeviceManagerDx.cpp
Normal file
878
gfx/thebes/DeviceManagerDx.cpp
Normal 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
|
||||
154
gfx/thebes/DeviceManagerDx.h
Normal file
154
gfx/thebes/DeviceManagerDx.h
Normal 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
26
gfx/thebes/DrawMode.h
Normal 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
159
gfx/thebes/PrintTarget.cpp
Normal 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
160
gfx/thebes/PrintTarget.h
Normal 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 */
|
||||
120
gfx/thebes/PrintTargetCG.cpp
Normal file
120
gfx/thebes/PrintTargetCG.cpp
Normal 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
|
||||
42
gfx/thebes/PrintTargetCG.h
Normal file
42
gfx/thebes/PrintTargetCG.h
Normal 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 */
|
||||
85
gfx/thebes/PrintTargetPDF.cpp
Normal file
85
gfx/thebes/PrintTargetPDF.cpp
Normal 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
|
||||
41
gfx/thebes/PrintTargetPDF.h
Normal file
41
gfx/thebes/PrintTargetPDF.h
Normal 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 */
|
||||
110
gfx/thebes/PrintTargetPS.cpp
Normal file
110
gfx/thebes/PrintTargetPS.cpp
Normal 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
|
||||
54
gfx/thebes/PrintTargetPS.h
Normal file
54
gfx/thebes/PrintTargetPS.h
Normal 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 */
|
||||
116
gfx/thebes/PrintTargetRecording.cpp
Normal file
116
gfx/thebes/PrintTargetRecording.cpp
Normal 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
|
||||
43
gfx/thebes/PrintTargetRecording.h
Normal file
43
gfx/thebes/PrintTargetRecording.h
Normal 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 */
|
||||
126
gfx/thebes/PrintTargetThebes.cpp
Normal file
126
gfx/thebes/PrintTargetThebes.cpp
Normal 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
|
||||
57
gfx/thebes/PrintTargetThebes.h
Normal file
57
gfx/thebes/PrintTargetThebes.h
Normal 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 */
|
||||
112
gfx/thebes/PrintTargetWindows.cpp
Normal file
112
gfx/thebes/PrintTargetWindows.cpp
Normal 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
|
||||
43
gfx/thebes/PrintTargetWindows.h
Normal file
43
gfx/thebes/PrintTargetWindows.h
Normal 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
43
gfx/thebes/RoundedRect.h
Normal 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
|
||||
150
gfx/thebes/SoftwareVsyncSource.cpp
Normal file
150
gfx/thebes/SoftwareVsyncSource.cpp
Normal 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;
|
||||
}
|
||||
62
gfx/thebes/SoftwareVsyncSource.h
Normal file
62
gfx/thebes/SoftwareVsyncSource.h
Normal 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
153
gfx/thebes/VsyncSource.cpp
Normal 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
84
gfx/thebes/VsyncSource.h
Normal 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 */
|
||||
119
gfx/thebes/cairo-xlib-utils.h
Normal file
119
gfx/thebes/cairo-xlib-utils.h
Normal 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_*/
|
||||
168
gfx/thebes/d3dkmtQueryStatistics.h
Normal file
168
gfx/thebes/d3dkmtQueryStatistics.h
Normal 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 *);
|
||||
}
|
||||
86
gfx/thebes/genLanguageTagList.pl
Normal file
86
gfx/thebes/genLanguageTagList.pl
Normal 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
22
gfx/thebes/genTables.py
Normal 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
77
gfx/thebes/gencjkcisvs.py
Normal 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
131
gfx/thebes/gfx2DGlue.h
Normal 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
620
gfx/thebes/gfxASurface.cpp
Normal 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
204
gfx/thebes/gfxASurface.h
Normal 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 */
|
||||
53
gfx/thebes/gfxAlphaRecovery.cpp
Normal file
53
gfx/thebes/gfxAlphaRecovery.cpp
Normal 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;
|
||||
}
|
||||
111
gfx/thebes/gfxAlphaRecovery.h
Normal file
111
gfx/thebes/gfxAlphaRecovery.h
Normal 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_ */
|
||||
235
gfx/thebes/gfxAlphaRecoverySSE2.cpp
Normal file
235
gfx/thebes/gfxAlphaRecoverySSE2.cpp
Normal 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;
|
||||
}
|
||||
338
gfx/thebes/gfxAndroidPlatform.cpp
Normal file
338
gfx/thebes/gfxAndroidPlatform.cpp
Normal 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();
|
||||
}
|
||||
85
gfx/thebes/gfxAndroidPlatform.h
Normal file
85
gfx/thebes/gfxAndroidPlatform.h
Normal 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 */
|
||||
|
||||
11
gfx/thebes/gfxBaseSharedMemorySurface.cpp
Normal file
11
gfx/thebes/gfxBaseSharedMemorySurface.cpp
Normal 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};
|
||||
|
||||
199
gfx/thebes/gfxBaseSharedMemorySurface.h
Normal file
199
gfx/thebes/gfxBaseSharedMemorySurface.h
Normal 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
1088
gfx/thebes/gfxBlur.cpp
Normal file
File diff suppressed because it is too large
Load diff
194
gfx/thebes/gfxBlur.h
Normal file
194
gfx/thebes/gfxBlur.h
Normal 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
83
gfx/thebes/gfxColor.h
Normal 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
1257
gfx/thebes/gfxContext.cpp
Normal file
File diff suppressed because it is too large
Load diff
694
gfx/thebes/gfxContext.h
Normal file
694
gfx/thebes/gfxContext.h
Normal 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 */
|
||||
800
gfx/thebes/gfxCoreTextShaper.cpp
Normal file
800
gfx/thebes/gfxCoreTextShaper.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
71
gfx/thebes/gfxCoreTextShaper.h
Normal file
71
gfx/thebes/gfxCoreTextShaper.h
Normal 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 */
|
||||
182
gfx/thebes/gfxDWriteCommon.cpp
Normal file
182
gfx/thebes/gfxDWriteCommon.cpp
Normal 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;
|
||||
}
|
||||
153
gfx/thebes/gfxDWriteCommon.h
Normal file
153
gfx/thebes/gfxDWriteCommon.h
Normal 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 */
|
||||
1838
gfx/thebes/gfxDWriteFontList.cpp
Normal file
1838
gfx/thebes/gfxDWriteFontList.cpp
Normal file
File diff suppressed because it is too large
Load diff
445
gfx/thebes/gfxDWriteFontList.h
Normal file
445
gfx/thebes/gfxDWriteFontList.h
Normal 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 */
|
||||
709
gfx/thebes/gfxDWriteFonts.cpp
Normal file
709
gfx/thebes/gfxDWriteFonts.cpp
Normal 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
107
gfx/thebes/gfxDWriteFonts.h
Normal 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
254
gfx/thebes/gfxDrawable.cpp
Normal 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
182
gfx/thebes/gfxDrawable.h
Normal 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
123
gfx/thebes/gfxEnv.h
Normal 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 */
|
||||
217
gfx/thebes/gfxFT2FontBase.cpp
Normal file
217
gfx/thebes/gfxFT2FontBase.cpp
Normal 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;
|
||||
}
|
||||
45
gfx/thebes/gfxFT2FontBase.h
Normal file
45
gfx/thebes/gfxFT2FontBase.h
Normal 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 */
|
||||
1608
gfx/thebes/gfxFT2FontList.cpp
Normal file
1608
gfx/thebes/gfxFT2FontList.cpp
Normal file
File diff suppressed because it is too large
Load diff
200
gfx/thebes/gfxFT2FontList.h
Normal file
200
gfx/thebes/gfxFT2FontList.h
Normal 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
227
gfx/thebes/gfxFT2Fonts.cpp
Normal 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
84
gfx/thebes/gfxFT2Fonts.h
Normal 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
378
gfx/thebes/gfxFT2Utils.cpp
Normal 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
94
gfx/thebes/gfxFT2Utils.h
Normal 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
25
gfx/thebes/gfxFailure.h
Normal 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_
|
||||
1866
gfx/thebes/gfxFcPlatformFontList.cpp
Normal file
1866
gfx/thebes/gfxFcPlatformFontList.cpp
Normal file
File diff suppressed because it is too large
Load diff
330
gfx/thebes/gfxFcPlatformFontList.h
Normal file
330
gfx/thebes/gfxFcPlatformFontList.h
Normal 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
4028
gfx/thebes/gfxFont.cpp
Normal file
File diff suppressed because it is too large
Load diff
2223
gfx/thebes/gfxFont.h
Normal file
2223
gfx/thebes/gfxFont.h
Normal file
File diff suppressed because it is too large
Load diff
237
gfx/thebes/gfxFontConstants.h
Normal file
237
gfx/thebes/gfxFontConstants.h
Normal 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
1831
gfx/thebes/gfxFontEntry.cpp
Normal file
File diff suppressed because it is too large
Load diff
777
gfx/thebes/gfxFontEntry.h
Normal file
777
gfx/thebes/gfxFontEntry.h
Normal 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
|
||||
364
gfx/thebes/gfxFontFamilyList.h
Normal file
364
gfx/thebes/gfxFontFamilyList.h
Normal 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 */
|
||||
81
gfx/thebes/gfxFontFeatures.cpp
Normal file
81
gfx/thebes/gfxFontFeatures.cpp
Normal 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);
|
||||
}
|
||||
|
||||
126
gfx/thebes/gfxFontFeatures.h
Normal file
126
gfx/thebes/gfxFontFeatures.h
Normal 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
|
||||
281
gfx/thebes/gfxFontInfoLoader.cpp
Normal file
281
gfx/thebes/gfxFontInfoLoader.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
258
gfx/thebes/gfxFontInfoLoader.h
Normal file
258
gfx/thebes/gfxFontInfoLoader.h
Normal 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 */
|
||||
288
gfx/thebes/gfxFontMissingGlyphs.cpp
Normal file
288
gfx/thebes/gfxFontMissingGlyphs.cpp
Normal 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;
|
||||
}
|
||||
55
gfx/thebes/gfxFontMissingGlyphs.h
Normal file
55
gfx/thebes/gfxFontMissingGlyphs.h
Normal 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
|
||||
36
gfx/thebes/gfxFontPrefLangList.h
Normal file
36
gfx/thebes/gfxFontPrefLangList.h
Normal 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)
|
||||
8
gfx/thebes/gfxFontTest.cpp
Normal file
8
gfx/thebes/gfxFontTest.cpp
Normal 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
84
gfx/thebes/gfxFontTest.h
Normal 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
1809
gfx/thebes/gfxFontUtils.cpp
Normal file
File diff suppressed because it is too large
Load diff
1027
gfx/thebes/gfxFontUtils.h
Normal file
1027
gfx/thebes/gfxFontUtils.h
Normal file
File diff suppressed because it is too large
Load diff
2262
gfx/thebes/gfxFontconfigFonts.cpp
Normal file
2262
gfx/thebes/gfxFontconfigFonts.cpp
Normal file
File diff suppressed because it is too large
Load diff
124
gfx/thebes/gfxFontconfigFonts.h
Normal file
124
gfx/thebes/gfxFontconfigFonts.h
Normal 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 */
|
||||
1100
gfx/thebes/gfxFontconfigUtils.cpp
Normal file
1100
gfx/thebes/gfxFontconfigUtils.cpp
Normal file
File diff suppressed because it is too large
Load diff
330
gfx/thebes/gfxFontconfigUtils.h
Normal file
330
gfx/thebes/gfxFontconfigUtils.h
Normal 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
564
gfx/thebes/gfxGDIFont.cpp
Normal 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
108
gfx/thebes/gfxGDIFont.h
Normal 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 */
|
||||
1195
gfx/thebes/gfxGDIFontList.cpp
Normal file
1195
gfx/thebes/gfxGDIFontList.cpp
Normal file
File diff suppressed because it is too large
Load diff
354
gfx/thebes/gfxGDIFontList.h
Normal file
354
gfx/thebes/gfxGDIFontList.h
Normal 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 */
|
||||
69
gfx/thebes/gfxGdkNativeRenderer.cpp
Normal file
69
gfx/thebes/gfxGdkNativeRenderer.cpp
Normal 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
|
||||
88
gfx/thebes/gfxGdkNativeRenderer.h
Normal file
88
gfx/thebes/gfxGdkNativeRenderer.h
Normal 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_*/
|
||||
154
gfx/thebes/gfxGlyphExtents.cpp
Normal file
154
gfx/thebes/gfxGlyphExtents.cpp
Normal 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);
|
||||
}
|
||||
151
gfx/thebes/gfxGlyphExtents.h
Normal file
151
gfx/thebes/gfxGlyphExtents.h
Normal 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
|
||||
239
gfx/thebes/gfxGradientCache.cpp
Normal file
239
gfx/thebes/gfxGradientCache.cpp
Normal 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
|
||||
36
gfx/thebes/gfxGradientCache.h
Normal file
36
gfx/thebes/gfxGradientCache.h
Normal 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
Loading…
Add table
Add a link
Reference in a new issue