aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/SkScalerContext.cpp
diff options
context:
space:
mode:
authorGravatar bungeman@google.com <bungeman@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-07-30 20:40:50 +0000
committerGravatar bungeman@google.com <bungeman@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-07-30 20:40:50 +0000
commit97efada074e4806479f1350ab1508939c2fdcb53 (patch)
tree374659467fad6bb854be612ec5bcb8398a44c97a /src/core/SkScalerContext.cpp
parentfa3d892f2e3beba39ccb9957be78aad529521740 (diff)
Gamma correcting masks.
Diffstat (limited to 'src/core/SkScalerContext.cpp')
-rw-r--r--src/core/SkScalerContext.cpp167
1 files changed, 83 insertions, 84 deletions
diff --git a/src/core/SkScalerContext.cpp b/src/core/SkScalerContext.cpp
index 7c26921bfb..9d99713c40 100644
--- a/src/core/SkScalerContext.cpp
+++ b/src/core/SkScalerContext.cpp
@@ -14,6 +14,7 @@
#include "SkFontHost.h"
#include "SkGlyph.h"
#include "SkMaskFilter.h"
+#include "SkMaskGamma.h"
#include "SkOrderedReadBuffer.h"
#include "SkPathEffect.h"
#include "SkRasterizer.h"
@@ -74,16 +75,16 @@ static SkFlattenable* load_flattenable(const SkDescriptor* desc, uint32_t tag) {
}
SkScalerContext::SkScalerContext(const SkDescriptor* desc)
- : fPathEffect(NULL), fMaskFilter(NULL)
+ : fRec(*static_cast<const Rec*>(desc->findEntry(kRec_SkDescriptorTag, NULL)))
+ , fBaseGlyphCount(0)
+ , fPathEffect(static_cast<SkPathEffect*>(load_flattenable(desc, kPathEffect_SkDescriptorTag)))
+ , fMaskFilter(static_cast<SkMaskFilter*>(load_flattenable(desc, kMaskFilter_SkDescriptorTag)))
+ , fRasterizer(static_cast<SkRasterizer*>(load_flattenable(desc, kRasterizer_SkDescriptorTag)))
+ // initialize based on our settings. subclasses can also force this
+ , fGenerateImageFromPath(fRec.fFrameWidth > 0 || fPathEffect != NULL || fRasterizer != NULL)
+ , fNextContext(NULL)
+ , fMaskPreBlend(SkScalerContext::GetMaskPreBlend(fRec))
{
- fBaseGlyphCount = 0;
- fNextContext = NULL;
-
- const Rec* rec = (const Rec*)desc->findEntry(kRec_SkDescriptorTag, NULL);
- SkASSERT(rec);
-
- fRec = *rec;
-
#ifdef DUMP_REC
desc->assertChecksum();
SkDebugf("SkScalarContext checksum %x count %d length %d\n",
@@ -98,14 +99,6 @@ SkScalerContext::SkScalerContext(const SkDescriptor* desc)
desc->findEntry(kPathEffect_SkDescriptorTag, NULL),
desc->findEntry(kMaskFilter_SkDescriptorTag, NULL));
#endif
-
- fPathEffect = (SkPathEffect*)load_flattenable(desc, kPathEffect_SkDescriptorTag);
- fMaskFilter = (SkMaskFilter*)load_flattenable(desc, kMaskFilter_SkDescriptorTag);
- fRasterizer = (SkRasterizer*)load_flattenable(desc, kRasterizer_SkDescriptorTag);
-
- // initialize based on our settings. subclasses can also force this
- fGenerateImageFromPath = fRec.fFrameWidth > 0 || fPathEffect != NULL ||
- fRasterizer != NULL;
}
SkScalerContext::~SkScalerContext() {
@@ -342,77 +335,39 @@ SK_ERROR:
glyph->fMaskFormat = fRec.fMaskFormat;
}
-#if 0 // UNUSED
-static bool isLCD(const SkScalerContext::Rec& rec) {
- return SkMask::kLCD16_Format == rec.fMaskFormat ||
- SkMask::kLCD32_Format == rec.fMaskFormat;
-}
-#endif
-
-#if 0 // UNUSED
-static uint16_t a8_to_rgb565(unsigned a8) {
- return SkPackRGB16(a8 >> 3, a8 >> 2, a8 >> 3);
-}
-
-static void copyToLCD16(const SkBitmap& src, const SkMask& dst) {
+template<bool APPLY_PREBLEND>
+static void pack3xHToLCD16(const SkBitmap& src, const SkMask& dst, SkMaskGamma::PreBlend* maskPreBlend) {
SkASSERT(SkBitmap::kA8_Config == src.config());
SkASSERT(SkMask::kLCD16_Format == dst.fFormat);
-
+
const int width = dst.fBounds.width();
const int height = dst.fBounds.height();
- const uint8_t* srcP = src.getAddr8(0, 0);
- size_t srcRB = src.rowBytes();
uint16_t* dstP = (uint16_t*)dst.fImage;
size_t dstRB = dst.fRowBytes;
- for (int y = 0; y < height; ++y) {
- for (int x = 0; x < width; ++x) {
- dstP[x] = a8_to_rgb565(srcP[x]);
- }
- srcP += srcRB;
- dstP = (uint16_t*)((char*)dstP + dstRB);
- }
-}
-#endif
-
-#define SK_FREETYPE_LCD_LERP 160
-
-static int lerp(int start, int end) {
- SkASSERT((unsigned)SK_FREETYPE_LCD_LERP <= 256);
- return start + ((end - start) * (SK_FREETYPE_LCD_LERP) >> 8);
-}
-
-static uint16_t packLCD16(unsigned r, unsigned g, unsigned b) {
- if (SK_FREETYPE_LCD_LERP) {
- // want (a+b+c)/3, but we approx to avoid the divide
- unsigned ave = (5 * (r + g + b) + g) >> 4;
- r = lerp(r, ave);
- g = lerp(g, ave);
- b = lerp(b, ave);
+
+ const uint8_t* maskPreBlendR = NULL;
+ const uint8_t* maskPreBlendG = NULL;
+ const uint8_t* maskPreBlendB = NULL;
+ if (APPLY_PREBLEND) {
+ maskPreBlendR = maskPreBlend->fR;
+ maskPreBlendG = maskPreBlend->fG;
+ maskPreBlendB = maskPreBlend->fB;
}
- return SkPackRGB16(r >> 3, g >> 2, b >> 3);
-}
-static void pack3xHToLCD16(const SkBitmap& src, const SkMask& dst) {
- SkASSERT(SkBitmap::kA8_Config == src.config());
- SkASSERT(SkMask::kLCD16_Format == dst.fFormat);
-
- const int width = dst.fBounds.width();
- const int height = dst.fBounds.height();
- uint16_t* dstP = (uint16_t*)dst.fImage;
- size_t dstRB = dst.fRowBytes;
for (int y = 0; y < height; ++y) {
const uint8_t* srcP = src.getAddr8(0, y);
for (int x = 0; x < width; ++x) {
- unsigned r = *srcP++;
- unsigned g = *srcP++;
- unsigned b = *srcP++;
- dstP[x] = packLCD16(r, g, b);
+ U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlendR);
+ U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlendG);
+ U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlendB);
+ dstP[x] = SkPack888ToRGB16(r, g, b);
}
dstP = (uint16_t*)((char*)dstP + dstRB);
}
}
-static void pack3xHToLCD32(const SkBitmap& src, const SkMask& dst) {
+template<bool APPLY_PREBLEND>
+static void pack3xHToLCD32(const SkBitmap& src, const SkMask& dst, SkMaskGamma::PreBlend* maskPreBlend) {
SkASSERT(SkBitmap::kA8_Config == src.config());
SkASSERT(SkMask::kLCD32_Format == dst.fFormat);
@@ -420,20 +375,29 @@ static void pack3xHToLCD32(const SkBitmap& src, const SkMask& dst) {
const int height = dst.fBounds.height();
SkPMColor* dstP = (SkPMColor*)dst.fImage;
size_t dstRB = dst.fRowBytes;
+
+ const uint8_t* maskPreBlendR = NULL;
+ const uint8_t* maskPreBlendG = NULL;
+ const uint8_t* maskPreBlendB = NULL;
+ if (APPLY_PREBLEND) {
+ maskPreBlendR = maskPreBlend->fR;
+ maskPreBlendG = maskPreBlend->fG;
+ maskPreBlendB = maskPreBlend->fB;
+ }
+
for (int y = 0; y < height; ++y) {
const uint8_t* srcP = src.getAddr8(0, y);
for (int x = 0; x < width; ++x) {
- unsigned r = *srcP++;
- unsigned g = *srcP++;
- unsigned b = *srcP++;
- unsigned a = SkMax32(SkMax32(r, g), b);
- dstP[x] = SkPackARGB32(a, r, g, b);
+ U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlendR);
+ U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlendG);
+ U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlendB);
+ dstP[x] = SkPackARGB32(0xFF, r, g, b);
}
dstP = (SkPMColor*)((char*)dstP + dstRB);
}
}
-static void generateMask(const SkMask& mask, const SkPath& path) {
+static void generateMask(const SkMask& mask, const SkPath& path, SkMaskGamma::PreBlend* maskPreBlend) {
SkBitmap::Config config;
SkPaint paint;
@@ -493,10 +457,18 @@ static void generateMask(const SkMask& mask, const SkPath& path) {
if (0 == dstRB) {
switch (mask.fFormat) {
case SkMask::kLCD16_Format:
- pack3xHToLCD16(bm, mask);
+ if (maskPreBlend) {
+ pack3xHToLCD16<true>(bm, mask, maskPreBlend);
+ } else {
+ pack3xHToLCD16<false>(bm, mask, maskPreBlend);
+ }
break;
case SkMask::kLCD32_Format:
- pack3xHToLCD32(bm, mask);
+ if (maskPreBlend) {
+ pack3xHToLCD32<true>(bm, mask, maskPreBlend);
+ } else {
+ pack3xHToLCD32<false>(bm, mask, maskPreBlend);
+ }
break;
default:
SkDEBUGFAIL("bad format for copyback");
@@ -504,10 +476,23 @@ static void generateMask(const SkMask& mask, const SkPath& path) {
}
}
+static void applyLUTToA8Glyph(const SkGlyph& glyph, const uint8_t* lut) {
+ uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage;
+ unsigned rowBytes = glyph.rowBytes();
+
+ for (int y = glyph.fHeight - 1; y >= 0; --y) {
+ for (int x = glyph.fWidth - 1; x >= 0; --x) {
+ dst[x] = lut[dst[x]];
+ }
+ dst += rowBytes;
+ }
+}
+
void SkScalerContext::getImage(const SkGlyph& origGlyph) {
const SkGlyph* glyph = &origGlyph;
SkGlyph tmpGlyph;
-
+ SkMaskGamma::PreBlend* maskPreBlend = &fMaskPreBlend;
+
if (fMaskFilter) { // restore the prefilter bounds
tmpGlyph.init(origGlyph.fID);
@@ -523,6 +508,7 @@ void SkScalerContext::getImage(const SkGlyph& origGlyph) {
SkASSERT(tmpGlyph.fWidth <= origGlyph.fWidth);
SkASSERT(tmpGlyph.fHeight <= origGlyph.fHeight);
glyph = &tmpGlyph;
+ maskPreBlend = NULL;
}
if (fGenerateImageFromPath) {
@@ -542,11 +528,19 @@ void SkScalerContext::getImage(const SkGlyph& origGlyph) {
SkMask::kJustRenderImage_CreateMode)) {
return;
}
+ //apply maskPreBlend to a8 (if not NULL)
+ if (maskPreBlend) {
+ applyLUTToA8Glyph(*glyph, maskPreBlend->fG);
+ }
} else {
- generateMask(mask, devPath);
+ generateMask(mask, devPath, maskPreBlend);
+ //apply maskPreBlend to a8 (if not NULL) -- already applied to lcd.
+ if (maskPreBlend && mask.fFormat == SkMask::kA8_Format) {
+ applyLUTToA8Glyph(*glyph, maskPreBlend->fG);
+ }
}
} else {
- this->getGlyphContext(*glyph)->generateImage(*glyph);
+ this->getGlyphContext(*glyph)->generateImage(*glyph, maskPreBlend);
}
if (fMaskFilter) {
@@ -581,6 +575,11 @@ void SkScalerContext::getImage(const SkGlyph& origGlyph) {
dst += dstRB;
}
SkMask::FreeImage(dstM.fImage);
+
+ /* Pre-blend is not currently applied to filtered text.
+ The primary filter is blur, for which contrast makes no sense,
+ and for which the destination guess error is more visible. */
+ //applyLUTToA8Glyph(origGlyph, fMaskPreBlend.fG);
}
}
}
@@ -749,7 +748,7 @@ protected:
virtual void generateMetrics(SkGlyph* glyph) {
glyph->zeroMetrics();
}
- virtual void generateImage(const SkGlyph& glyph) {}
+ virtual void generateImage(const SkGlyph& glyph, SkMaskGamma::PreBlend* maskPreBlend) {}
virtual void generatePath(const SkGlyph& glyph, SkPath* path) {}
virtual void generateFontMetrics(SkPaint::FontMetrics* mx,
SkPaint::FontMetrics* my) {