From a767fa06ca28be9df1ff6e08a299e0bec839a2dc Mon Sep 17 00:00:00 2001 From: "reed@google.com" Date: Fri, 5 Aug 2011 21:40:26 +0000 Subject: [PATCH] add api for scalers to force us to use skia to generate their bits from their path. This may allow the windows scaler to do that if GDI is giving bad results (i.e. not respecting the request for antialiasing). git-svn-id: http://skia.googlecode.com/svn/trunk@2054 2bbb7eff-a529-9590-31e7-b0007b416f81 --- include/core/SkScalerContext.h | 6 +++ src/core/SkScalerContext.cpp | 95 ++++++++++++++++++++++++++++++---- src/ports/SkFontHost_win.cpp | 46 ++++++++++++---- 3 files changed, 125 insertions(+), 22 deletions(-) diff --git a/include/core/SkScalerContext.h b/include/core/SkScalerContext.h index 0299ccb5b4..fe59b2a18e 100644 --- a/include/core/SkScalerContext.h +++ b/include/core/SkScalerContext.h @@ -262,12 +262,18 @@ protected: // default impl returns 0, indicating failure. virtual SkUnichar generateGlyphToChar(uint16_t); + void forceGenerateImageFromPath() { fGenerateImageFromPath = true; } + private: SkPathEffect* fPathEffect; SkMaskFilter* fMaskFilter; SkRasterizer* fRasterizer; SkScalar fDevFrameWidth; + // if this is set, we draw the image from a path, rather than + // calling generateImage. + bool fGenerateImageFromPath; + void internalGetPath(const SkGlyph& glyph, SkPath* fillPath, SkPath* devPath, SkMatrix* fillToDevMatrix); diff --git a/src/core/SkScalerContext.cpp b/src/core/SkScalerContext.cpp index ff2f0ccaa4..3c50128cc0 100644 --- a/src/core/SkScalerContext.cpp +++ b/src/core/SkScalerContext.cpp @@ -112,6 +112,10 @@ SkScalerContext::SkScalerContext(const SkDescriptor* desc) 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() { @@ -254,7 +258,7 @@ void SkScalerContext::getMetrics(SkGlyph* glyph) { return; } - if (fRec.fFrameWidth > 0 || fPathEffect != NULL || fRasterizer != NULL) { + if (fGenerateImageFromPath) { SkPath devPath, fillPath; SkMatrix fillToDevMatrix; @@ -322,6 +326,51 @@ SK_ERROR: glyph->fMaskFormat = fRec.fMaskFormat; } +static bool isLCD(const SkScalerContext::Rec& rec) { + return SkMask::kLCD16_Format == rec.fMaskFormat || + SkMask::kLCD32_Format == rec.fMaskFormat; +} + +static uint16_t a8_to_rgb565(unsigned a8) { + return SkPackRGB16(a8 >> 3, a8 >> 2, a8 >> 3); +} + +static void copyToLCD16(const SkBitmap& src, const SkGlyph& dst) { + SkASSERT(SkBitmap::kA8_Config == src.config()); + SkASSERT(SkMask::kLCD16_Format == dst.fMaskFormat); + + const uint8_t* srcP = src.getAddr8(0, 0); + size_t srcRB = src.rowBytes(); + uint16_t* dstP = (uint16_t*)dst.fImage; + size_t dstRB = dst.rowBytes(); + for (int y = 0; y < dst.fHeight; ++y) { + for (int x = 0; x < dst.fWidth; ++x) { + dstP[x] = a8_to_rgb565(srcP[x]); + } + srcP += srcRB; + dstP = (uint16_t*)((char*)dstP + dstRB); + } +} + +static void copyToLCD32(const SkBitmap& src, const SkGlyph& dst) { + SkASSERT(SkBitmap::kA8_Config == src.config()); + SkASSERT(SkMask::kLCD32_Format == dst.fMaskFormat); + + const uint8_t* srcP = src.getAddr8(0, 0); + size_t srcRB = src.rowBytes(); + SkPMColor* dstP = (SkPMColor*)dst.fImage; + size_t dstRB = dst.rowBytes(); + for (int y = 0; y < dst.fHeight; ++y) { + for (int x = 0; x < dst.fWidth; ++x) { + unsigned a8 = srcP[x]; + a8 = a8 | (a8 << 8); + dstP[x] = a8 | (a8 << 16); + } + srcP += srcRB; + dstP = (SkPMColor*)((char*)dstP + dstRB); + } +} + void SkScalerContext::getImage(const SkGlyph& origGlyph) { const SkGlyph* glyph = &origGlyph; SkGlyph tmpGlyph; @@ -343,7 +392,7 @@ void SkScalerContext::getImage(const SkGlyph& origGlyph) { glyph = &tmpGlyph; } - if (fRec.fFrameWidth > 0 || fPathEffect != NULL || fRasterizer != NULL) { + if (fGenerateImageFromPath) { SkPath devPath, fillPath; SkMatrix fillToDevMatrix; @@ -367,15 +416,14 @@ void SkScalerContext::getImage(const SkGlyph& origGlyph) { SkMatrix matrix; SkRegion clip; SkPaint paint; - SkDraw draw; - if (SkMask::kA8_Format == fRec.fMaskFormat) { - config = SkBitmap::kA8_Config; - paint.setAntiAlias(true); - } else { - SkASSERT(SkMask::kBW_Format == fRec.fMaskFormat); + if (SkMask::kBW_Format == fRec.fMaskFormat) { config = SkBitmap::kA1_Config; paint.setAntiAlias(false); + } else { + config = SkBitmap::kA8_Config; + paint.setAntiAlias(true); + } clip.setRect(0, 0, glyph->fWidth, glyph->fHeight); @@ -383,14 +431,39 @@ void SkScalerContext::getImage(const SkGlyph& origGlyph) { -SkIntToScalar(glyph->fTop)); bm.setConfig(config, glyph->fWidth, glyph->fHeight, glyph->rowBytes()); - bm.setPixels(glyph->fImage); - sk_bzero(glyph->fImage, bm.height() * bm.rowBytes()); + bool needCopyBack; + if (isLCD(fRec)) { + bm.allocPixels(); + bm.lockPixels(); + needCopyBack = true; + } else { + bm.setPixels(glyph->fImage); + needCopyBack = false; + } + sk_bzero(bm.getPixels(), bm.getSafeSize()); + + SkDraw draw; + sk_bzero(&draw, sizeof(draw)); draw.fClip = &clip; draw.fMatrix = &matrix; draw.fBitmap = &bm; - draw.fBounder = NULL; draw.drawPath(devPath, paint); + + // for now we just upscale A8 to lcd. Later we may respect the LCD + // request by scaling horizontally/vertiacally 3x + if (needCopyBack) { + switch (fRec.fMaskFormat) { + case SkMask::kLCD16_Format: + copyToLCD16(bm, *glyph); + break; + case SkMask::kLCD32_Format: + copyToLCD32(bm, *glyph); + break; + default: + SkASSERT(!"bad format for copyback"); + } + } } } else { this->getGlyphContext(*glyph)->generateImage(*glyph); diff --git a/src/ports/SkFontHost_win.cpp b/src/ports/SkFontHost_win.cpp index 5ece09e25d..f292bdd69b 100755 --- a/src/ports/SkFontHost_win.cpp +++ b/src/ports/SkFontHost_win.cpp @@ -23,6 +23,11 @@ #include "tchar.h" #include "Usp10.h" +// define this in your Makefile or .gyp to enforce AA requests +// which GDI ignores at small sizes. This flag guarantees AA +// for rotated text, regardless of GDI's notions. +//#define SK_ENFORCE_ROTATED_TEXT_AA_ON_WINDOWS + // client3d has to undefine this for now #define CAN_USE_LOGFONT_NAME @@ -31,6 +36,32 @@ static bool isLCD(const SkScalerContext::Rec& rec) { SkMask::kLCD32_Format == rec.fMaskFormat; } +static bool bothZero(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) { + return 0 == rec.fPreSkewX && + (bothZero(rec.fPost2x2[0][1], rec.fPost2x2[1][0]) || + bothZero(rec.fPost2x2[0][0], rec.fPost2x2[1][1])); +} + +static bool needToRenderWithSkia(const SkScalerContext::Rec& rec) { +#ifdef SK_ENFORCE_ROTATED_TEXT_AA_ON_WINDOWS + // What we really want to catch is when GDI will ignore the AA request and give + // us BW instead. Smallish rotated text is one heuristic, so this code is just + // an approximation. We shouldn't need to do this for larger sizes, but at those + // sizes, the quality difference gets less and less between our general + // scanconverter and GDI's. + if (SkMask::kA8_Format == rec.fMaskFormat && !isAxisAligned(rec)) { + return true; + } +#endif + // false means allow GDI to generate the bits + return false; +} + using namespace skia_advanced_typeface_metrics_utils; static const uint16_t BUFFERSIZE = (16384 - 32); @@ -347,6 +378,10 @@ SkScalerContext_Windows::SkScalerContext_Windows(const SkDescriptor* desc) fHiResMatrix.preScale(scale, scale); } fSavefont = (HFONT)SelectObject(fDDC, fFont); + + if (needToRenderWithSkia(fRec)) { + this->forceGenerateImageFromPath(); + } } SkScalerContext_Windows::~SkScalerContext_Windows() { @@ -948,17 +983,6 @@ SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) { return NULL; } -static bool bothZero(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) { - return 0 == rec.fPreSkewX && - (bothZero(rec.fPost2x2[0][1], rec.fPost2x2[1][0]) || - bothZero(rec.fPost2x2[0][0], rec.fPost2x2[1][1])); -} - void SkFontHost::FilterRec(SkScalerContext::Rec* rec) { unsigned flagsWeDontSupport = SkScalerContext::kDevKernText_Flag | SkScalerContext::kAutohinting_Flag |