diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi index aca36b568b..52a9c3a3e1 100644 --- a/gyp/gpu.gypi +++ b/gyp/gpu.gypi @@ -82,6 +82,8 @@ '<(skia_src_path)/gpu/GrCoordTransform.cpp', '<(skia_src_path)/gpu/GrDefaultGeoProcFactory.cpp', '<(skia_src_path)/gpu/GrDefaultGeoProcFactory.h', + '<(skia_src_path)/gpu/GrDistanceFieldAdjustTable.cpp', + '<(skia_src_path)/gpu/GrDistanceFieldAdjustTable.h', '<(skia_src_path)/gpu/GrDrawContext.cpp', '<(skia_src_path)/gpu/GrDrawingManager.cpp', '<(skia_src_path)/gpu/GrDrawingManager.h', diff --git a/src/gpu/GrAtlasTextContext.cpp b/src/gpu/GrAtlasTextContext.cpp index 153d0fe516..38b9c042d4 100644 --- a/src/gpu/GrAtlasTextContext.cpp +++ b/src/gpu/GrAtlasTextContext.cpp @@ -46,13 +46,11 @@ static const int kLargeDFFontLimit = 384; #else static const int kLargeDFFontLimit = 2 * kLargeDFFontSize; #endif - -SkDEBUGCODE(static const int kExpectedDistanceAdjustTableSize = 8;) }; GrAtlasTextContext::GrAtlasTextContext(GrContext* context, const SkSurfaceProps& surfaceProps) : INHERITED(context, surfaceProps) - , fDistanceAdjustTable(new DistanceAdjustTable) { + , fDistanceAdjustTable(new GrDistanceFieldAdjustTable) { // We overallocate vertices in our textblobs based on the assumption that A8 has the greatest // vertexStride static_assert(GrAtlasTextBlob::kGrayTextVASize >= GrAtlasTextBlob::kColorTextVASize && @@ -62,87 +60,6 @@ GrAtlasTextContext::GrAtlasTextContext(GrContext* context, const SkSurfaceProps& fCache = context->getTextBlobCache(); } -void GrAtlasTextContext::DistanceAdjustTable::buildDistanceAdjustTable() { - - // This is used for an approximation of the mask gamma hack, used by raster and bitmap - // text. The mask gamma hack is based off of guessing what the blend color is going to - // be, and adjusting the mask so that when run through the linear blend will - // produce the value closest to the desired result. However, in practice this means - // that the 'adjusted' mask is just increasing or decreasing the coverage of - // the mask depending on what it is thought it will blit against. For black (on - // assumed white) this means that coverages are decreased (on a curve). For white (on - // assumed black) this means that coverages are increased (on a a curve). At - // middle (perceptual) gray (which could be blit against anything) the coverages - // remain the same. - // - // The idea here is that instead of determining the initial (real) coverage and - // then adjusting that coverage, we determine an adjusted coverage directly by - // essentially manipulating the geometry (in this case, the distance to the glyph - // edge). So for black (on assumed white) this thins a bit; for white (on - // assumed black) this fake bolds the geometry a bit. - // - // The distance adjustment is calculated by determining the actual coverage value which - // when fed into in the mask gamma table gives us an 'adjusted coverage' value of 0.5. This - // actual coverage value (assuming it's between 0 and 1) corresponds to a distance from the - // actual edge. So by subtracting this distance adjustment and computing without the - // the coverage adjustment we should get 0.5 coverage at the same point. - // - // This has several implications: - // For non-gray lcd smoothed text, each subpixel essentially is using a - // slightly different geometry. - // - // For black (on assumed white) this may not cover some pixels which were - // previously covered; however those pixels would have been only slightly - // covered and that slight coverage would have been decreased anyway. Also, some pixels - // which were previously fully covered may no longer be fully covered. - // - // For white (on assumed black) this may cover some pixels which weren't - // previously covered at all. - - int width, height; - size_t size; - -#ifdef SK_GAMMA_CONTRAST - SkScalar contrast = SK_GAMMA_CONTRAST; -#else - SkScalar contrast = 0.5f; -#endif - SkScalar paintGamma = SK_GAMMA_EXPONENT; - SkScalar deviceGamma = SK_GAMMA_EXPONENT; - - size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma, - &width, &height); - - SkASSERT(kExpectedDistanceAdjustTableSize == height); - fTable = new SkScalar[height]; - - SkAutoTArray data((int)size); - SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get()); - - // find the inverse points where we cross 0.5 - // binsearch might be better, but we only need to do this once on creation - for (int row = 0; row < height; ++row) { - uint8_t* rowPtr = data.get() + row*width; - for (int col = 0; col < width - 1; ++col) { - if (rowPtr[col] <= 127 && rowPtr[col + 1] >= 128) { - // compute point where a mask value will give us a result of 0.5 - float interp = (127.5f - rowPtr[col]) / (rowPtr[col + 1] - rowPtr[col]); - float borderAlpha = (col + interp) / 255.f; - - // compute t value for that alpha - // this is an approximate inverse for smoothstep() - float t = borderAlpha*(borderAlpha*(4.0f*borderAlpha - 6.0f) + 5.0f) / 3.0f; - - // compute distance which gives us that t value - const float kDistanceFieldAAFactor = 0.65f; // should match SK_DistanceFieldAAFactor - float d = 2.0f*kDistanceFieldAAFactor*t - kDistanceFieldAAFactor; - - fTable[row] = d; - break; - } - } - } -} GrAtlasTextContext* GrAtlasTextContext::Create(GrContext* context, const SkSurfaceProps& surfaceProps) { diff --git a/src/gpu/GrAtlasTextContext.h b/src/gpu/GrAtlasTextContext.h index 20f747ff15..82283bcf24 100644 --- a/src/gpu/GrAtlasTextContext.h +++ b/src/gpu/GrAtlasTextContext.h @@ -11,6 +11,7 @@ #include "GrTextContext.h" #include "GrAtlasTextBlob.h" +#include "GrDistanceFieldAdjustTable.h" #include "GrGeometryProcessor.h" #include "SkTextBlobRunIterator.h" @@ -148,29 +149,9 @@ private: const SkPoint& offset, const SkIRect& regionClipBounds); - // Distance field text needs this table to compute a value for use in the fragment shader. - // Because the GrAtlasTextContext can go out of scope before the final flush, this needs to be - // refcnted and malloced - struct DistanceAdjustTable : public SkNVRefCnt { - DistanceAdjustTable() { this->buildDistanceAdjustTable(); } - ~DistanceAdjustTable() { delete[] fTable; } - - const SkScalar& operator[] (int i) const { - return fTable[i]; - } - - private: - void buildDistanceAdjustTable(); - - SkScalar* fTable; - }; - GrBatchTextStrike* fCurrStrike; GrTextBlobCache* fCache; - SkAutoTUnref fDistanceAdjustTable; - - friend class GrTextBlobCache; - friend class GrAtlasTextBatch; + SkAutoTUnref fDistanceAdjustTable; #ifdef GR_TEST_UTILS DRAW_BATCH_TEST_FRIEND(TextBlobBatch); diff --git a/src/gpu/GrDistanceFieldAdjustTable.cpp b/src/gpu/GrDistanceFieldAdjustTable.cpp new file mode 100644 index 0000000000..1c5aeceb80 --- /dev/null +++ b/src/gpu/GrDistanceFieldAdjustTable.cpp @@ -0,0 +1,93 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrDistanceFieldAdjustTable.h" + +#include "SkScalerContext.h" + +SkDEBUGCODE(static const int kExpectedDistanceAdjustTableSize = 8;) + +void GrDistanceFieldAdjustTable::buildDistanceAdjustTable() { + // This is used for an approximation of the mask gamma hack, used by raster and bitmap + // text. The mask gamma hack is based off of guessing what the blend color is going to + // be, and adjusting the mask so that when run through the linear blend will + // produce the value closest to the desired result. However, in practice this means + // that the 'adjusted' mask is just increasing or decreasing the coverage of + // the mask depending on what it is thought it will blit against. For black (on + // assumed white) this means that coverages are decreased (on a curve). For white (on + // assumed black) this means that coverages are increased (on a a curve). At + // middle (perceptual) gray (which could be blit against anything) the coverages + // remain the same. + // + // The idea here is that instead of determining the initial (real) coverage and + // then adjusting that coverage, we determine an adjusted coverage directly by + // essentially manipulating the geometry (in this case, the distance to the glyph + // edge). So for black (on assumed white) this thins a bit; for white (on + // assumed black) this fake bolds the geometry a bit. + // + // The distance adjustment is calculated by determining the actual coverage value which + // when fed into in the mask gamma table gives us an 'adjusted coverage' value of 0.5. This + // actual coverage value (assuming it's between 0 and 1) corresponds to a distance from the + // actual edge. So by subtracting this distance adjustment and computing without the + // the coverage adjustment we should get 0.5 coverage at the same point. + // + // This has several implications: + // For non-gray lcd smoothed text, each subpixel essentially is using a + // slightly different geometry. + // + // For black (on assumed white) this may not cover some pixels which were + // previously covered; however those pixels would have been only slightly + // covered and that slight coverage would have been decreased anyway. Also, some pixels + // which were previously fully covered may no longer be fully covered. + // + // For white (on assumed black) this may cover some pixels which weren't + // previously covered at all. + + int width, height; + size_t size; + +#ifdef SK_GAMMA_CONTRAST + SkScalar contrast = SK_GAMMA_CONTRAST; +#else + SkScalar contrast = 0.5f; +#endif + SkScalar paintGamma = SK_GAMMA_EXPONENT; + SkScalar deviceGamma = SK_GAMMA_EXPONENT; + + size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma, + &width, &height); + + SkASSERT(kExpectedDistanceAdjustTableSize == height); + fTable = new SkScalar[height]; + + SkAutoTArray data((int)size); + SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get()); + + // find the inverse points where we cross 0.5 + // binsearch might be better, but we only need to do this once on creation + for (int row = 0; row < height; ++row) { + uint8_t* rowPtr = data.get() + row*width; + for (int col = 0; col < width - 1; ++col) { + if (rowPtr[col] <= 127 && rowPtr[col + 1] >= 128) { + // compute point where a mask value will give us a result of 0.5 + float interp = (127.5f - rowPtr[col]) / (rowPtr[col + 1] - rowPtr[col]); + float borderAlpha = (col + interp) / 255.f; + + // compute t value for that alpha + // this is an approximate inverse for smoothstep() + float t = borderAlpha*(borderAlpha*(4.0f*borderAlpha - 6.0f) + 5.0f) / 3.0f; + + // compute distance which gives us that t value + const float kDistanceFieldAAFactor = 0.65f; // should match SK_DistanceFieldAAFactor + float d = 2.0f*kDistanceFieldAAFactor*t - kDistanceFieldAAFactor; + + fTable[row] = d; + break; + } + } + } +} diff --git a/src/gpu/GrDistanceFieldAdjustTable.h b/src/gpu/GrDistanceFieldAdjustTable.h new file mode 100644 index 0000000000..f7d8bee089 --- /dev/null +++ b/src/gpu/GrDistanceFieldAdjustTable.h @@ -0,0 +1,31 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrDistanceFieldAdjustTable_DEFINED +#define GrDistanceFieldAdjustTable_DEFINED + +#include "SkRefCnt.h" +#include "SkScalar.h" + +// Distance field text needs this table to compute a value for use in the fragment shader. +// Because the GrAtlasTextContext can go out of scope before the final flush, this needs to be +// refcnted and malloced +struct GrDistanceFieldAdjustTable : public SkNVRefCnt { + GrDistanceFieldAdjustTable() { this->buildDistanceAdjustTable(); } + ~GrDistanceFieldAdjustTable() { delete[] fTable; } + + const SkScalar& operator[] (int i) const { + return fTable[i]; + } + +private: + void buildDistanceAdjustTable(); + + SkScalar* fTable; +}; + +#endif diff --git a/src/gpu/batches/GrAtlasTextBatch.h b/src/gpu/batches/GrAtlasTextBatch.h index 5fd3d6110f..edbe3d7813 100644 --- a/src/gpu/batches/GrAtlasTextBatch.h +++ b/src/gpu/batches/GrAtlasTextBatch.h @@ -11,6 +11,7 @@ #include "batches/GrVertexBatch.h" #include "GrAtlasTextContext.h" +#include "GrDistanceFieldAdjustTable.h" class GrAtlasTextBatch : public GrVertexBatch { public: @@ -19,7 +20,6 @@ public: static const int kVerticesPerGlyph = GrAtlasTextBlob::kVerticesPerGlyph; static const int kIndicesPerGlyph = 6; - typedef GrAtlasTextContext::DistanceAdjustTable DistanceAdjustTable; typedef GrAtlasTextBlob Blob; typedef Blob::Run Run; typedef Run::SubRunInfo TextInfo; @@ -56,10 +56,11 @@ public: return batch; } - static GrAtlasTextBatch* CreateDistanceField(int glyphCount, GrBatchFontCache* fontCache, - const DistanceAdjustTable* distanceAdjustTable, - SkColor filteredColor, bool isLCD, - bool useBGR) { + static GrAtlasTextBatch* CreateDistanceField( + int glyphCount, GrBatchFontCache* fontCache, + const GrDistanceFieldAdjustTable* distanceAdjustTable, + SkColor filteredColor, bool isLCD, + bool useBGR) { GrAtlasTextBatch* batch = new GrAtlasTextBatch; batch->fFontCache = fontCache; @@ -196,7 +197,7 @@ private: GrBatchFontCache* fFontCache; // Distance field properties - SkAutoTUnref fDistanceAdjustTable; + SkAutoTUnref fDistanceAdjustTable; SkColor fFilteredColor; typedef GrVertexBatch INHERITED;