Better rendering detection with DirectWrite.
When requesting embedded bitmaps, all sizes in a gridfit gasp range with a bitmap in that range should be gridfit to the fullest extent possible. R=eae@chromium.org, reed@google.com, mtklein@google.com Author: bungeman@google.com Review URL: https://codereview.chromium.org/341343002
This commit is contained in:
parent
0156c7646c
commit
740c3f17f7
@ -17,6 +17,7 @@
|
||||
'../src/sfnt/SkOTTable_EBDT.h',
|
||||
'../src/sfnt/SkOTTable_EBLC.h',
|
||||
'../src/sfnt/SkOTTable_EBSC.h',
|
||||
'../src/sfnt/SkOTTable_gasp.h',
|
||||
'../src/sfnt/SkOTTable_glyf.h',
|
||||
'../src/sfnt/SkOTTable_head.h',
|
||||
'../src/sfnt/SkOTTable_hhea.h',
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "SkMatrix22.h"
|
||||
#include "SkOTTable_EBLC.h"
|
||||
#include "SkOTTable_EBSC.h"
|
||||
#include "SkOTTable_gasp.h"
|
||||
#include "SkPath.h"
|
||||
#include "SkScalerContext.h"
|
||||
#include "SkScalerContext_win_dw.h"
|
||||
@ -30,7 +31,57 @@ static bool isLCD(const SkScalerContext::Rec& rec) {
|
||||
SkMask::kLCD32_Format == rec.fMaskFormat;
|
||||
}
|
||||
|
||||
static bool hasBitmapStrike(DWriteFontTypeface* typeface, int size) {
|
||||
/** A PPEMRange is inclusive, [min, max]. */
|
||||
struct PPEMRange {
|
||||
int min;
|
||||
int max;
|
||||
};
|
||||
|
||||
/** If the rendering mode for the specified 'size' is gridfit, then place
|
||||
* the gridfit range into 'range'. Otherwise, leave 'range' alone.
|
||||
*/
|
||||
static void expand_range_if_gridfit_only(DWriteFontTypeface* typeface, int size, PPEMRange* range) {
|
||||
AutoTDWriteTable<SkOTTableGridAndScanProcedure> gasp(typeface->fDWriteFontFace.get());
|
||||
if (!gasp.fExists) {
|
||||
return;
|
||||
}
|
||||
if (gasp.fSize < sizeof(SkOTTableGridAndScanProcedure)) {
|
||||
return;
|
||||
}
|
||||
if (gasp->version != SkOTTableGridAndScanProcedure::version0 &&
|
||||
gasp->version != SkOTTableGridAndScanProcedure::version1)
|
||||
{
|
||||
return ;
|
||||
}
|
||||
|
||||
uint16_t numRanges = SkEndianSwap16(gasp->numRanges);
|
||||
if (numRanges > 1024 ||
|
||||
gasp.fSize < sizeof(SkOTTableGridAndScanProcedure) +
|
||||
sizeof(SkOTTableGridAndScanProcedure::GaspRange) * numRanges)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const SkOTTableGridAndScanProcedure::GaspRange* rangeTable =
|
||||
SkTAfter<const SkOTTableGridAndScanProcedure::GaspRange>(gasp.get());
|
||||
int minPPEM = -1;
|
||||
for (uint16_t i = 0; i < numRanges; ++i, ++rangeTable) {
|
||||
int maxPPEM = SkEndianSwap16(rangeTable->maxPPEM);
|
||||
// Test that the size is in range and the range is gridfit only.
|
||||
if (minPPEM < size && size <= maxPPEM &&
|
||||
rangeTable->flags.raw.value == SkOTTableGridAndScanProcedure::GaspRange::behavior::Raw::GridfitMask)
|
||||
{
|
||||
range->min = minPPEM + 1;
|
||||
range->max = maxPPEM;
|
||||
return;
|
||||
}
|
||||
minPPEM = maxPPEM;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static bool has_bitmap_strike(DWriteFontTypeface* typeface, PPEMRange range) {
|
||||
{
|
||||
AutoTDWriteTable<SkOTTableEmbeddedBitmapLocation> eblc(typeface->fDWriteFontFace.get());
|
||||
if (!eblc.fExists) {
|
||||
@ -44,7 +95,8 @@ static bool hasBitmapStrike(DWriteFontTypeface* typeface, int size) {
|
||||
}
|
||||
|
||||
uint32_t numSizes = SkEndianSwap32(eblc->numSizes);
|
||||
if (eblc.fSize < sizeof(SkOTTableEmbeddedBitmapLocation) +
|
||||
if (numSizes > 1024 ||
|
||||
eblc.fSize < sizeof(SkOTTableEmbeddedBitmapLocation) +
|
||||
sizeof(SkOTTableEmbeddedBitmapLocation::BitmapSizeTable) * numSizes)
|
||||
{
|
||||
return false;
|
||||
@ -53,13 +105,15 @@ static bool hasBitmapStrike(DWriteFontTypeface* typeface, int size) {
|
||||
const SkOTTableEmbeddedBitmapLocation::BitmapSizeTable* sizeTable =
|
||||
SkTAfter<const SkOTTableEmbeddedBitmapLocation::BitmapSizeTable>(eblc.get());
|
||||
for (uint32_t i = 0; i < numSizes; ++i, ++sizeTable) {
|
||||
if (sizeTable->ppemX == size && sizeTable->ppemY == size) {
|
||||
if (sizeTable->ppemX == sizeTable->ppemY &&
|
||||
range.min <= sizeTable->ppemX && sizeTable->ppemX <= range.max)
|
||||
{
|
||||
// TODO: determine if we should dig through IndexSubTableArray/IndexSubTable
|
||||
// to determine the actual number of glyphs with bitmaps.
|
||||
|
||||
// TODO: Ensure that the bitmaps actually cover a significant portion of the strike.
|
||||
|
||||
//TODO: Endure that the bitmaps are bi-level.
|
||||
// TODO: Ensure that the bitmaps are bi-level?
|
||||
if (sizeTable->endGlyphIndex >= sizeTable->startGlyphIndex + 3) {
|
||||
return true;
|
||||
}
|
||||
@ -80,7 +134,8 @@ static bool hasBitmapStrike(DWriteFontTypeface* typeface, int size) {
|
||||
}
|
||||
|
||||
uint32_t numSizes = SkEndianSwap32(ebsc->numSizes);
|
||||
if (ebsc.fSize < sizeof(SkOTTableEmbeddedBitmapScaling) +
|
||||
if (numSizes > 1024 ||
|
||||
ebsc.fSize < sizeof(SkOTTableEmbeddedBitmapScaling) +
|
||||
sizeof(SkOTTableEmbeddedBitmapScaling::BitmapScaleTable) * numSizes)
|
||||
{
|
||||
return false;
|
||||
@ -89,7 +144,8 @@ static bool hasBitmapStrike(DWriteFontTypeface* typeface, int size) {
|
||||
const SkOTTableEmbeddedBitmapScaling::BitmapScaleTable* scaleTable =
|
||||
SkTAfter<const SkOTTableEmbeddedBitmapScaling::BitmapScaleTable>(ebsc.get());
|
||||
for (uint32_t i = 0; i < numSizes; ++i, ++scaleTable) {
|
||||
if (scaleTable->ppemX == size && scaleTable->ppemY == size) {
|
||||
if (scaleTable->ppemX == scaleTable->ppemY &&
|
||||
range.min <= scaleTable->ppemX && scaleTable->ppemX <= range.max) {
|
||||
// EBSC tables are normally only found in bitmap only fonts.
|
||||
return true;
|
||||
}
|
||||
@ -99,15 +155,15 @@ static bool hasBitmapStrike(DWriteFontTypeface* typeface, int size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool bothZero(SkScalar a, SkScalar b) {
|
||||
static bool both_zero(SkScalar a, SkScalar b) {
|
||||
return 0 == a && 0 == b;
|
||||
}
|
||||
|
||||
// returns false if there is any non-90-rotation or skew
|
||||
static bool isAxisAligned(const SkScalerContext::Rec& rec) {
|
||||
static bool is_axis_aligned(const SkScalerContext::Rec& rec) {
|
||||
return 0 == rec.fPreSkewX &&
|
||||
(bothZero(rec.fPost2x2[0][1], rec.fPost2x2[1][0]) ||
|
||||
bothZero(rec.fPost2x2[0][0], rec.fPost2x2[1][1]));
|
||||
(both_zero(rec.fPost2x2[0][1], rec.fPost2x2[1][0]) ||
|
||||
both_zero(rec.fPost2x2[0][0], rec.fPost2x2[1][1]));
|
||||
}
|
||||
|
||||
SkScalerContext_DW::SkScalerContext_DW(DWriteFontTypeface* typeface,
|
||||
@ -159,11 +215,19 @@ SkScalerContext_DW::SkScalerContext_DW(DWriteFontTypeface* typeface,
|
||||
}
|
||||
|
||||
bool bitmapRequested = SkToBool(fRec.fFlags & SkScalerContext::kEmbeddedBitmapText_Flag);
|
||||
bool hasBitmap = false;
|
||||
bool treatLikeBitmap = false;
|
||||
bool axisAlignedBitmap = false;
|
||||
if (bitmapRequested) {
|
||||
hasBitmap = hasBitmapStrike(typeface, SkScalarTruncToInt(gdiTextSize));
|
||||
axisAlignedBitmap = isAxisAligned(fRec);
|
||||
// When embedded bitmaps are requested, treat the entire range like
|
||||
// a bitmap strike if the range is gridfit only and contains a bitmap.
|
||||
int bitmapPPEM = SkScalarTruncToInt(gdiTextSize);
|
||||
PPEMRange range = { bitmapPPEM, bitmapPPEM };
|
||||
#ifndef SK_IGNORE_DWRITE_RENDERING_FIX
|
||||
expand_range_if_gridfit_only(typeface, bitmapPPEM, &range);
|
||||
#endif
|
||||
treatLikeBitmap = has_bitmap_strike(typeface, range);
|
||||
|
||||
axisAlignedBitmap = is_axis_aligned(fRec);
|
||||
}
|
||||
|
||||
// If the user requested aliased, do so with aliased compatible metrics.
|
||||
@ -176,7 +240,7 @@ SkScalerContext_DW::SkScalerContext_DW(DWriteFontTypeface* typeface,
|
||||
|
||||
// If we can use a bitmap, use gdi classic rendering and measurement.
|
||||
// This will not always provide a bitmap, but matches expected behavior.
|
||||
} else if (hasBitmap && axisAlignedBitmap) {
|
||||
} else if (treatLikeBitmap && axisAlignedBitmap) {
|
||||
fTextSizeRender = gdiTextSize;
|
||||
fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC;
|
||||
fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
|
||||
@ -185,7 +249,7 @@ SkScalerContext_DW::SkScalerContext_DW(DWriteFontTypeface* typeface,
|
||||
|
||||
// If rotated but the horizontal text could have used a bitmap,
|
||||
// render high quality rotated glyphs but measure using bitmap metrics.
|
||||
} else if (hasBitmap) {
|
||||
} else if (treatLikeBitmap) {
|
||||
fTextSizeRender = gdiTextSize;
|
||||
fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
|
||||
fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
|
||||
|
73
src/sfnt/SkOTTable_gasp.h
Normal file
73
src/sfnt/SkOTTable_gasp.h
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2014 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkOTTable_gasp_DEFINED
|
||||
#define SkOTTable_gasp_DEFINED
|
||||
|
||||
#include "SkEndian.h"
|
||||
#include "SkOTTableTypes.h"
|
||||
#include "SkTypedEnum.h"
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
struct SkOTTableGridAndScanProcedure {
|
||||
static const SK_OT_CHAR TAG0 = 'g';
|
||||
static const SK_OT_CHAR TAG1 = 'a';
|
||||
static const SK_OT_CHAR TAG2 = 's';
|
||||
static const SK_OT_CHAR TAG3 = 'p';
|
||||
static const SK_OT_ULONG TAG = SkOTTableTAG<SkOTTableGridAndScanProcedure>::value;
|
||||
|
||||
SK_OT_USHORT version;
|
||||
static const SK_OT_USHORT version0 = SkTEndian_SwapBE16(0);
|
||||
static const SK_OT_USHORT version1 = SkTEndian_SwapBE16(1);
|
||||
|
||||
SK_OT_USHORT numRanges;
|
||||
|
||||
struct GaspRange {
|
||||
SK_OT_USHORT maxPPEM;
|
||||
union behavior {
|
||||
struct Field {
|
||||
//8-15
|
||||
SK_OT_BYTE_BITFIELD(
|
||||
Reserved08,
|
||||
Reserved09,
|
||||
Reserved10,
|
||||
Reserved11,
|
||||
Reserved12,
|
||||
Reserved13,
|
||||
Reserved14,
|
||||
Reserved15)
|
||||
//0-7
|
||||
SK_OT_BYTE_BITFIELD(
|
||||
Gridfit,
|
||||
DoGray,
|
||||
SymmetricGridfit, // Version 1
|
||||
SymmetricSmoothing, // Version 1
|
||||
Reserved04,
|
||||
Reserved05,
|
||||
Reserved06,
|
||||
Reserved07)
|
||||
} field;
|
||||
struct Raw {
|
||||
static const SK_OT_USHORT GridfitMask = SkTEndian_SwapBE16(1 << 0);
|
||||
static const SK_OT_USHORT DoGrayMask = SkTEndian_SwapBE16(1 << 1);
|
||||
static const SK_OT_USHORT SymmetricGridfitMask = SkTEndian_SwapBE16(1 << 2);
|
||||
static const SK_OT_USHORT SymmetricSmoothingMask = SkTEndian_SwapBE16(1 << 3);
|
||||
SK_OT_USHORT value;
|
||||
} raw;
|
||||
} flags;
|
||||
}; //gaspRange[numRanges]
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
SK_COMPILE_ASSERT(offsetof(SkOTTableGridAndScanProcedure, numRanges) == 2, SkOTTableGridAndScanProcedure_numRanges_not_at_2);
|
||||
SK_COMPILE_ASSERT(sizeof(SkOTTableGridAndScanProcedure) == 4, sizeof_SkOTTableGridAndScanProcedure_not_4);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user