diff options
author | Derek Sollenberger <djsollen@google.com> | 2014-07-25 11:37:09 -0400 |
---|---|---|
committer | Derek Sollenberger <djsollen@google.com> | 2014-07-25 17:10:54 +0000 |
commit | 9ed58377b45739aa8f9dc1b02049ab29c137a838 (patch) | |
tree | c03142d59a99bbc3d74aaf3e5e838fb8a0804260 | |
parent | 7c4fd9b27bf4c140c63e3861f48fe1cfc12d0fae (diff) | |
download | skia-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.gyp | 1 | ||||
-rwxr-xr-x | src/effects/SkColorFilterImageFilter.cpp | 2 | ||||
-rw-r--r-- | src/ports/SkScalerContext_win_dw.cpp | 128 | ||||
-rw-r--r-- | src/sfnt/SkOTTable_gasp.h | 73 | ||||
-rw-r--r-- | tests/ImageFilterTest.cpp | 27 |
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); |