aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDerek Sollenberger <djsollen@google.com>2014-07-25 11:37:09 -0400
committerDerek Sollenberger <djsollen@google.com>2014-07-25 17:10:54 +0000
commit9ed58377b45739aa8f9dc1b02049ab29c137a838 (patch)
treec03142d59a99bbc3d74aaf3e5e838fb8a0804260
parent7c4fd9b27bf4c140c63e3861f48fe1cfc12d0fae (diff)
downloadskia-9ed58377b45739aa8f9dc1b02049ab29c137a838.tar.gz
Merge changes from Skia's m37 branch into lmp-dev.
This merge includes the following CLs... 20ee1ba Change SkCanvasState to use inheritance. b572f07 DirectWrite to detect GDI only fonts 282aa1e Fix SkColorFilterImageFilter matrix optimization e849224 Re-land "Fix external SkImageFilter caching with clips." 9b1eeeb Better rendering detection with DirectWrite.
-rw-r--r--gyp/sfnt.gyp1
-rwxr-xr-xsrc/effects/SkColorFilterImageFilter.cpp2
-rw-r--r--src/ports/SkScalerContext_win_dw.cpp128
-rw-r--r--src/sfnt/SkOTTable_gasp.h73
-rw-r--r--tests/ImageFilterTest.cpp27
5 files changed, 215 insertions, 16 deletions
diff --git a/gyp/sfnt.gyp b/gyp/sfnt.gyp
index f5ac70e694..89a1fbddcc 100644
--- a/gyp/sfnt.gyp
+++ b/gyp/sfnt.gyp
@@ -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',
diff --git a/src/effects/SkColorFilterImageFilter.cpp b/src/effects/SkColorFilterImageFilter.cpp
index 8cdd546b99..f2490e3273 100755
--- a/src/effects/SkColorFilterImageFilter.cpp
+++ b/src/effects/SkColorFilterImageFilter.cpp
@@ -68,7 +68,7 @@ SkColorFilterImageFilter* SkColorFilterImageFilter::Create(SkColorFilter* cf,
SkAutoUnref autoUnref(inputColorFilter);
if (inputColorFilter->asColorMatrix(inputMatrix) && !matrix_needs_clamping(inputMatrix)) {
SkScalar combinedMatrix[20];
- mult_color_matrix(inputMatrix, colorMatrix, combinedMatrix);
+ mult_color_matrix(colorMatrix, inputMatrix, combinedMatrix);
SkAutoTUnref<SkColorFilter> newCF(SkColorMatrixFilter::Create(combinedMatrix));
return SkNEW_ARGS(SkColorFilterImageFilter, (newCF, input->getInput(0), cropRect));
}
diff --git a/src/ports/SkScalerContext_win_dw.cpp b/src/ports/SkScalerContext_win_dw.cpp
index f3293bf003..f9f4392b75 100644
--- a/src/ports/SkScalerContext_win_dw.cpp
+++ b/src/ports/SkScalerContext_win_dw.cpp
@@ -17,6 +17,8 @@
#include "SkMatrix22.h"
#include "SkOTTable_EBLC.h"
#include "SkOTTable_EBSC.h"
+#include "SkOTTable_gasp.h"
+#include "SkOTTable_maxp.h"
#include "SkPath.h"
#include "SkScalerContext.h"
#include "SkScalerContext_win_dw.h"
@@ -30,7 +32,78 @@ static bool isLCD(const SkScalerContext::Rec& rec) {
SkMask::kLCD32_Format == rec.fMaskFormat;
}
-static bool hasBitmapStrike(DWriteFontTypeface* typeface, int size) {
+static bool is_hinted_without_gasp(DWriteFontTypeface* typeface) {
+ AutoTDWriteTable<SkOTTableMaximumProfile> maxp(typeface->fDWriteFontFace.get());
+ if (!maxp.fExists) {
+ return false;
+ }
+ if (maxp.fSize < sizeof(SkOTTableMaximumProfile::Version::TT)) {
+ return false;
+ }
+ if (maxp->version.version != SkOTTableMaximumProfile::Version::TT::VERSION) {
+ return false;
+ }
+
+ if (0 == maxp->version.tt.maxSizeOfInstructions) {
+ // No hints.
+ return false;
+ }
+
+ AutoTDWriteTable<SkOTTableGridAndScanProcedure> gasp(typeface->fDWriteFontFace.get());
+ return !gasp.fExists;
+}
+
+/** 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 +117,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 +127,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 +156,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 +166,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 +177,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 +237,17 @@ 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 };
+ expand_range_if_gridfit_only(typeface, bitmapPPEM, &range);
+ treatLikeBitmap = has_bitmap_strike(typeface, range);
+
+ axisAlignedBitmap = is_axis_aligned(fRec);
}
// If the user requested aliased, do so with aliased compatible metrics.
@@ -176,7 +260,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,13 +269,24 @@ 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;
fTextSizeMeasure = gdiTextSize;
fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;
+ // Fonts that have hints but no gasp table get non-symmetric rendering.
+ // Usually such fonts have low quality hints which were never tested
+ // with anything but GDI ClearType classic. Such fonts often rely on
+ // drop out control in the y direction in order to be legible.
+ } else if (is_hinted_without_gasp(typeface)) {
+ fTextSizeRender = gdiTextSize;
+ fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL;
+ fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
+ fTextSizeMeasure = realTextSize;
+ fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
+
// The normal case is to use natural symmetric rendering and linear metrics.
} else {
fTextSizeRender = realTextSize;
@@ -292,6 +387,9 @@ void SkScalerContext_DW::generateAdvance(SkGlyph* glyph) {
if (DWRITE_MEASURING_MODE_GDI_CLASSIC == fMeasuringMode ||
DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode)
{
+ // DirectWrite produced 'compatible' metrics, but while close,
+ // the end result is not always an integer as it would be with GDI.
+ vecs[0].fX = SkScalarRoundToScalar(advanceX);
fG_inv.mapVectors(vecs, SK_ARRAY_COUNT(vecs));
} else {
fSkXform.mapVectors(vecs, SK_ARRAY_COUNT(vecs));
diff --git a/src/sfnt/SkOTTable_gasp.h b/src/sfnt/SkOTTable_gasp.h
new file mode 100644
index 0000000000..ca2d265e22
--- /dev/null
+++ b/src/sfnt/SkOTTable_gasp.h
@@ -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
diff --git a/tests/ImageFilterTest.cpp b/tests/ImageFilterTest.cpp
index cd6d235dc5..4a1cdf2e93 100644
--- a/tests/ImageFilterTest.cpp
+++ b/tests/ImageFilterTest.cpp
@@ -159,6 +159,33 @@ DEF_TEST(ImageFilter, reporter) {
}
{
+ // Check that two non-commutative matrices are concatenated in
+ // the correct order.
+ SkScalar blueToRedMatrix[20] = { 0 };
+ blueToRedMatrix[2] = blueToRedMatrix[18] = SK_Scalar1;
+ SkScalar redToGreenMatrix[20] = { 0 };
+ redToGreenMatrix[5] = redToGreenMatrix[18] = SK_Scalar1;
+ SkAutoTUnref<SkColorFilter> blueToRed(SkColorMatrixFilter::Create(blueToRedMatrix));
+ SkAutoTUnref<SkImageFilter> filter1(SkColorFilterImageFilter::Create(blueToRed.get()));
+ SkAutoTUnref<SkColorFilter> redToGreen(SkColorMatrixFilter::Create(redToGreenMatrix));
+ SkAutoTUnref<SkImageFilter> filter2(SkColorFilterImageFilter::Create(redToGreen.get(), filter1.get()));
+
+ SkBitmap result;
+ result.allocN32Pixels(kBitmapSize, kBitmapSize);
+
+ SkPaint paint;
+ paint.setColor(SK_ColorBLUE);
+ paint.setImageFilter(filter2.get());
+ SkCanvas canvas(result);
+ canvas.clear(0x0);
+ SkRect rect = SkRect::Make(SkIRect::MakeWH(kBitmapSize, kBitmapSize));
+ canvas.drawRect(rect, paint);
+ uint32_t pixel = *result.getAddr32(0, 0);
+ // The result here should be green, since we have effectively shifted blue to green.
+ REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
+ }
+
+ {
// Tests pass by not asserting
SkBitmap bitmap, result;
make_small_bitmap(bitmap);