diff options
author | Mike Reed <reed@google.com> | 2009-07-24 15:53:43 -0400 |
---|---|---|
committer | Mike Reed <reed@google.com> | 2009-07-27 12:40:11 -0400 |
commit | af2616552738d653d5453915d3236e7154b868cd (patch) | |
tree | 2adf051e4585ac1afd72b3c91cdd0ed1e0bfb438 | |
parent | 8db6227fff273d07b62767a9f25dc5a625bd9f63 (diff) | |
download | external_skia-af2616552738d653d5453915d3236e7154b868cd.zip external_skia-af2616552738d653d5453915d3236e7154b868cd.tar.gz external_skia-af2616552738d653d5453915d3236e7154b868cd.tar.bz2 |
refresh from trunk
check-point for lcd text rendering
-rw-r--r-- | include/core/SkFontHost.h | 46 | ||||
-rw-r--r-- | include/core/SkMask.h | 50 | ||||
-rw-r--r-- | include/core/SkPaint.h | 56 | ||||
-rw-r--r-- | include/core/SkScalerContext.h | 41 | ||||
-rw-r--r-- | include/core/SkUserConfig.h | 1 | ||||
-rw-r--r-- | src/core/SkBitmapProcState.cpp | 23 | ||||
-rw-r--r-- | src/core/SkBitmapProcState_shaderproc.h | 27 | ||||
-rw-r--r-- | src/core/SkBlitter_ARGB32.cpp | 104 | ||||
-rw-r--r-- | src/core/SkCanvas.cpp | 32 | ||||
-rw-r--r-- | src/core/SkEdge.cpp | 5 | ||||
-rw-r--r-- | src/core/SkPaint.cpp | 45 | ||||
-rw-r--r-- | src/core/SkPath.cpp | 4 | ||||
-rw-r--r-- | src/core/SkScalerContext.cpp | 66 | ||||
-rw-r--r-- | src/core/SkScan_Path.cpp | 3 | ||||
-rw-r--r-- | src/ports/SkFontHost_FreeType.cpp | 140 |
15 files changed, 509 insertions, 134 deletions
diff --git a/include/core/SkFontHost.h b/include/core/SkFontHost.h index 03ebfc7..5a49146 100644 --- a/include/core/SkFontHost.h +++ b/include/core/SkFontHost.h @@ -131,6 +131,20 @@ public: /////////////////////////////////////////////////////////////////////////// + /** Given a filled-out rec, the fonthost may decide to modify it to reflect + what the host is actually capable of fulfilling. For example, if the + rec is requesting a level of hinting that, for this host, maps some + other level (e.g. kFull -> kNormal), it should update the rec to reflect + what will actually be done. This is an optimization so that the font + cache does not contain different recs (i.e. keys) that in reality map to + the same output. + + A lazy (but valid) fonthost can do nothing in its FilterRec routine. + */ + static void FilterRec(SkScalerContext::Rec* rec); + + /////////////////////////////////////////////////////////////////////////// + /** Return the number of tables in the font */ static int CountTables(SkFontID); @@ -187,6 +201,38 @@ public: white (table[1]) gamma tables. */ static void GetGammaTables(const uint8_t* tables[2]); + + /////////////////////////////////////////////////////////////////////////// + + /** LCDs either have their color elements arranged horizontally or + vertically. When rendering subpixel glyphs we need to know which way + round they are. + + Note, if you change this after startup, you'll need to flush the glyph + cache because it'll have the wrong type of masks cached. + */ + enum LCDOrientation { + kHorizontal_LCDOrientation = 0, //!< this is the default + kVertical_LCDOrientation = 1, + }; + + static void SetSubpixelOrientation(LCDOrientation orientation); + static LCDOrientation GetSubpixelOrientation(); + + /** LCD color elements can vary in order. For subpixel text we need to know + the order which the LCDs uses so that the color fringes are in the + correct place. + + Note, if you change this after startup, you'll need to flush the glyph + cache because it'll have the wrong type of masks cached. + */ + enum LCDOrder { + kRGB_LCDOrder = 0, //!< this is the default + kBGR_LCDOrder = 1, + }; + + static void SetSubpixelOrder(LCDOrder order); + static LCDOrder GetSubpixelOrder(); }; #endif diff --git a/include/core/SkMask.h b/include/core/SkMask.h index b72a5c5..6cbf37a 100644 --- a/include/core/SkMask.h +++ b/include/core/SkMask.h @@ -28,11 +28,28 @@ struct SkMask { kBW_Format, //!< 1bit per pixel mask (e.g. monochrome) kA8_Format, //!< 8bits per pixel mask (e.g. antialiasing) k3D_Format, //!< 3 8bit per pixl planes: alpha, mul, add - kLCD_Format //!< 3 bytes/pixel: r/g/b + + /* The LCD formats look like this in memory: + + First, there's an A8 plane which contains the average alpha value for + each pixel. Because of this, the LCD formats can be passed directly + to functions which expect an A8 and everything will just work. + + After that in memory, there's a bitmap of 32-bit values which have + been RGB order corrected for the current screen (based on the + settings in SkFontHost at the time of renderering). The alpha value + for each pixel is the maximum of the three alpha values. + + kHorizontalLCD_Format has an extra column of pixels on the left and right + edges. kVerticalLCD_Format has an extra row at the top and bottom. + */ + + kHorizontalLCD_Format, //!< 4 bytes/pixel: a/r/g/b + kVerticalLCD_Format, //!< 4 bytes/pixel: a/r/g/b }; - + enum { - kCountMaskFormats = kLCD_Format + 1 + kCountMaskFormats = kVerticalLCD_Format + 1 }; uint8_t* fImage; @@ -78,6 +95,29 @@ struct SkMask { return fImage + x - fBounds.fLeft + (y - fBounds.fTop) * fRowBytes; } + /** Return an address into the 32-bit plane of an LCD or VerticalLCD mask + for the given position. + */ + const uint32_t* getAddrLCD(int x, int y) const { + SkASSERT(fFormat == kHorizontalLCD_Format || fFormat == kVerticalLCD_Format); + SkASSERT(fBounds.contains(x, y)); + SkASSERT(fImage != NULL); + + return reinterpret_cast<const uint32_t*>(fImage + SkAlign4(fRowBytes * fBounds.height())) + + x - fBounds.fLeft + (y - fBounds.fTop) * rowWordsLCD(); + } + + /** Return the number of 32-bit words in a row of the 32-bit plane of an + LCD or VerticalLCD mask. + */ + unsigned rowWordsLCD() const { + SkASSERT(fFormat == kHorizontalLCD_Format || fFormat == kVerticalLCD_Format); + if (fFormat == kHorizontalLCD_Format) + return fBounds.width() + 2; + else + return fBounds.width(); + } + static uint8_t* AllocImage(size_t bytes); static void FreeImage(void* image); @@ -86,6 +126,10 @@ struct SkMask { kJustRenderImage_CreateMode, //!< render into preallocate mask kComputeBoundsAndRenderImage_CreateMode //!< compute bounds, alloc image and render into it }; + + static bool FormatIsLCD(Format fm) { + return kHorizontalLCD_Format == fm || kVerticalLCD_Format == fm; + } }; #endif diff --git a/include/core/SkPaint.h b/include/core/SkPaint.h index 5a01cd6..ed60a20 100644 --- a/include/core/SkPaint.h +++ b/include/core/SkPaint.h @@ -69,6 +69,33 @@ public: */ void reset(); + /** Specifies the level of hinting to be performed. These names are taken + from the Gnome/Cairo names for the same. They are translated into + Freetype concepts the same as in cairo-ft-font.c: + kNo_Hinting -> FT_LOAD_NO_HINTING + kSlight_Hinting -> FT_LOAD_TARGET_LIGHT + kNormal_Hinting -> <default, no option> + kFull_Hinting -> <same as kNormalHinting, unless we are rendering + subpixel glyphs, in which case TARGET_LCD or + TARGET_LCD_V is used> + */ + enum Hinting { + kNo_Hinting = 0, + kSlight_Hinting = 1, + kNormal_Hinting = 2, //!< this is the default + kFull_Hinting = 3, + }; + + Hinting getHinting() const + { + return static_cast<Hinting>(fHinting); + } + + void setHinting(Hinting hintingLevel) + { + fHinting = hintingLevel; + } + /** Specifies the bit values that are stored in the paint's flags. */ enum Flags { @@ -79,10 +106,13 @@ public: kStrikeThruText_Flag = 0x10, //!< mask to enable strike-thru text kFakeBoldText_Flag = 0x20, //!< mask to enable fake-bold text kLinearText_Flag = 0x40, //!< mask to enable linear-text - kSubpixelText_Flag = 0x80, //!< mask to enable subpixel-text + kSubpixelText_Flag = 0x80, //!< mask to enable subpixel text positioning kDevKernText_Flag = 0x100, //!< mask to enable device kerning text + kLCDRenderText_Flag = 0x200, //!< mask to enable subpixel glyph renderering + // when adding extra flags, note that the fFlags member is specified + // with a bit-width and you'll have to expand it. - kAllFlags = 0x1FF + kAllFlags = 0x3FF }; /** Return the paint's flags. Use the Flag enum to test flag values. @@ -143,12 +173,23 @@ public: return SkToBool(this->getFlags() & kSubpixelText_Flag); } - /** Helper for setFlags(), setting or clearing the kSubpixelText_Flag bit - @param subpixelText true to set the subpixelText bit in the paint's - flags, false to clear it. + /** Helper for setFlags(), setting or clearing the kSubpixelText_Flag + bit @param subpixelText true to set the subpixelText bit in the paint's flags, + false to clear it. */ void setSubpixelText(bool subpixelText); - + + bool isLCDRenderText() const + { + return SkToBool(this->getFlags() & kLCDRenderText_Flag); + } + + /** Helper for setFlags(), setting or clearing the kLCDRenderText_Flag bit + @param subpixelRender true to set the subpixelRenderText bit in the paint's flags, + false to clear it. + */ + void setLCDRenderText(bool subpixelRender); + /** Helper for getFlags(), returning true if kUnderlineText_Flag bit is set @return true if the underlineText bit is set in the paint's flags. */ @@ -749,12 +790,13 @@ private: SkColor fColor; SkScalar fWidth; SkScalar fMiterLimit; - unsigned fFlags : 9; + unsigned fFlags : 10; unsigned fTextAlign : 2; unsigned fCapType : 2; unsigned fJoinType : 2; unsigned fStyle : 2; unsigned fTextEncoding : 2; // 3 values + unsigned fHinting : 2; SkDrawCacheProc getDrawCacheProc() const; SkMeasureCacheProc getMeasureCacheProc(TextBufferDirection dir, diff --git a/include/core/SkScalerContext.h b/include/core/SkScalerContext.h index b06a443..29c28f7 100644 --- a/include/core/SkScalerContext.h +++ b/include/core/SkScalerContext.h @@ -136,34 +136,59 @@ struct SkGlyph { } void toMask(SkMask* mask) const; + + /** Given a glyph which is has a mask format of LCD or VerticalLCD, take + the A8 plane in fImage and produce a valid LCD plane from it. + */ + void expandA8ToLCD() const; }; class SkScalerContext { public: - enum Hints { - kNo_Hints, - kSubpixel_Hints, - kNormal_Hints - }; enum Flags { kFrameAndFill_Flag = 0x01, kDevKernText_Flag = 0x02, kGammaForBlack_Flag = 0x04, // illegal to set both Gamma flags - kGammaForWhite_Flag = 0x08 // illegal to set both Gamma flags + kGammaForWhite_Flag = 0x08, // illegal to set both Gamma flags + // together, these two flags resulting in a two bit value which matches + // up with the SkPaint::Hinting enum. + kHintingBit1_Flag = 0x10, + kHintingBit2_Flag = 0x20, + }; +private: + enum { + kHintingMask = kHintingBit1_Flag | kHintingBit2_Flag }; +public: struct Rec { uint32_t fFontID; SkScalar fTextSize, fPreScaleX, fPreSkewX; SkScalar fPost2x2[2][2]; SkScalar fFrameWidth, fMiterLimit; - uint8_t fHints; + bool fSubpixelPositioning; uint8_t fMaskFormat; uint8_t fStrokeJoin; uint8_t fFlags; - + void getMatrixFrom2x2(SkMatrix*) const; void getLocalMatrix(SkMatrix*) const; void getSingleMatrix(SkMatrix*) const; + + SkPaint::Hinting getHinting() const { + return static_cast<SkPaint::Hinting>((fFlags & kHintingMask) >> 4); + } + + void setHinting(SkPaint::Hinting hinting) { + fFlags = (fFlags & ~kHintingMask) | (hinting << 4); + } + + SkMask::Format getFormat() const { + return static_cast<SkMask::Format>(fMaskFormat); + } + + bool isLCD() const { + return SkMask::FormatIsLCD(this->getFormat()); + } }; SkScalerContext(const SkDescriptor* desc); diff --git a/include/core/SkUserConfig.h b/include/core/SkUserConfig.h index e5528cc..a72aa1a 100644 --- a/include/core/SkUserConfig.h +++ b/include/core/SkUserConfig.h @@ -135,7 +135,6 @@ void Android_SkDebugf(const char* file, int line, const char* function, const char* format, ...); - /* If SK_DEBUG is defined, then you can optionally define SK_SUPPORT_UNITTEST which will run additional self-tests at startup. These can take a long time, so this flag is optional. diff --git a/src/core/SkBitmapProcState.cpp b/src/core/SkBitmapProcState.cpp index dbdcc27..379a7b3 100644 --- a/src/core/SkBitmapProcState.cpp +++ b/src/core/SkBitmapProcState.cpp @@ -302,6 +302,20 @@ SkASSERT(state.fAlphaScale < 256) #define FILTER_TO_DST(c) SkCompact_rgb_16((c) >> 5) #include "SkBitmapProcState_shaderproc.h" + +#define TILEX_PROCF(fx, max) (((fx) & 0xFFFF) * ((max) + 1) >> 16) +#define TILEY_PROCF(fy, max) (((fy) & 0xFFFF) * ((max) + 1) >> 16) +#define TILEX_LOW_BITS(fx, max) ((((fx) & 0xFFFF) * ((max) + 1) >> 12) & 0xF) +#define TILEY_LOW_BITS(fy, max) ((((fy) & 0xFFFF) * ((max) + 1) >> 12) & 0xF) + +#define MAKENAME(suffix) Repeat_S16_D16 ## suffix +#define SRCTYPE uint16_t +#define DSTTYPE uint16_t +#define CHECKSTATE(state) SkASSERT(state.fBitmap->config() == SkBitmap::kRGB_565_Config) +#define SRC_TO_FILTER(src) src +#define FILTER_TO_DST(c) SkCompact_rgb_16((c) >> 5) +#include "SkBitmapProcState_shaderproc.h" + /////////////////////////////////////////////////////////////////////////////// static bool valid_for_filtering(unsigned dimension) { @@ -485,8 +499,13 @@ bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) { fSampleProc16 = gSample16[index]; // our special-case shaderprocs - if (clamp_clamp && (7 == index)) { - fShaderProc16 = Clamp_S16_D16_filter_DX_shaderproc; + if (S16_D16_filter_DX == fSampleProc16) { + if (clamp_clamp) { + fShaderProc16 = Clamp_S16_D16_filter_DX_shaderproc; + } else if (SkShader::kRepeat_TileMode == fTileModeX && + SkShader::kRepeat_TileMode == fTileModeY) { + fShaderProc16 = Repeat_S16_D16_filter_DX_shaderproc; + } } return true; } diff --git a/src/core/SkBitmapProcState_shaderproc.h b/src/core/SkBitmapProcState_shaderproc.h index b0384e7..070a62b 100644 --- a/src/core/SkBitmapProcState_shaderproc.h +++ b/src/core/SkBitmapProcState_shaderproc.h @@ -1,14 +1,5 @@ #define SCALE_FILTER_NAME MAKENAME(_filter_DX_shaderproc) -#ifndef PREAMBLE - #define PREAMBLE(state) - #define PREAMBLE_PARAM_X - #define PREAMBLE_PARAM_Y - #define PREAMBLE_ARG_X - #define PREAMBLE_ARG_Y -#endif - - static void SCALE_FILTER_NAME(const SkBitmapProcState& s, int x, int y, DSTTYPE* SK_RESTRICT colors, int count) { SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | @@ -18,8 +9,6 @@ static void SCALE_FILTER_NAME(const SkBitmapProcState& s, int x, int y, SkASSERT(s.fDoFilter); SkDEBUGCODE(CHECKSTATE(s);) - PREAMBLE(s); - const unsigned maxX = s.fBitmap->width() - 1; const SkFixed oneX = s.fFilterOneX; const SkFixed dx = s.fInvSx; @@ -65,19 +54,15 @@ static void SCALE_FILTER_NAME(const SkBitmapProcState& s, int x, int y, /////////////////////////////////////////////////////////////////////////////// -#undef MAKENAME #undef TILEX_PROCF #undef TILEY_PROCF #undef TILEX_LOW_BITS #undef TILEY_LOW_BITS -#ifdef CHECK_FOR_DECAL - #undef CHECK_FOR_DECAL -#endif +#undef MAKENAME +#undef SRCTYPE +#undef DSTTYPE +#undef CHECKSTATE +#undef SRC_TO_FILTER +#undef FILTER_TO_DST #undef SCALE_FILTER_NAME - -#undef PREAMBLE -#undef PREAMBLE_PARAM_X -#undef PREAMBLE_PARAM_Y -#undef PREAMBLE_ARG_X -#undef PREAMBLE_ARG_Y diff --git a/src/core/SkBlitter_ARGB32.cpp b/src/core/SkBlitter_ARGB32.cpp index ed2fc0e..d3e5714 100644 --- a/src/core/SkBlitter_ARGB32.cpp +++ b/src/core/SkBlitter_ARGB32.cpp @@ -21,6 +21,25 @@ #include "SkUtils.h" #include "SkXfermode.h" +#if defined(SK_SUPPORT_LCDTEXT) +namespace skia_blitter_support { +// subpixel helper functions from SkBlitter_ARGB32_Subpixel.cpp +uint32_t* adjustForSubpixelClip(const SkMask& mask, + const SkIRect& clip, const SkBitmap& device, + int* widthAdjustment, int* heightAdjustment, + const uint32_t** alpha32); +extern uint32_t BlendLCDPixelWithColor(const uint32_t alphaPixel, const uint32_t originalPixel, + const uint32_t sourcePixel); +extern uint32_t BlendLCDPixelWithOpaqueColor(const uint32_t alphaPixel, const uint32_t originalPixel, + const uint32_t sourcePixel); +extern uint32_t BlendLCDPixelWithBlack(const uint32_t alphaPixel, const uint32_t originalPixel); +} + +using namespace skia_blitter_support; +#endif + +////////////////////////////////////////////////////////////////////////////////////// + SkARGB32_Blitter::SkARGB32_Blitter(const SkBitmap& device, const SkPaint& paint) : INHERITED(device) { uint32_t color = paint.getColor(); @@ -201,12 +220,45 @@ void SkARGB32_Opaque_Blitter::blitMask(const SkMask& mask, int width = clip.width(); int height = clip.height(); - uint32_t* device = fDevice.getAddr32(x, y); - const uint8_t* alpha = mask.getAddr(x, y); +#if defined(SK_SUPPORT_LCDTEXT) + const bool lcdMode = mask.fFormat == SkMask::kHorizontalLCD_Format; + const bool verticalLCDMode = mask.fFormat == SkMask::kVerticalLCD_Format; +#endif + + // In LCD mode the masks have either an extra couple of rows or columns on the edges. uint32_t srcColor = fPMColor; - unsigned devRB = fDevice.rowBytes() - (width << 2); - unsigned maskRB = mask.fRowBytes - width; +#if defined(SK_SUPPORT_LCDTEXT) + if (lcdMode || verticalLCDMode) { + int widthAdjustment, heightAdjustment; + const uint32_t* alpha32; + uint32_t* device = adjustForSubpixelClip(mask, clip, fDevice, &widthAdjustment, &heightAdjustment, &alpha32); + + width += widthAdjustment; + height += heightAdjustment; + + unsigned devRB = fDevice.rowBytes() - (width << 2); + unsigned alphaExtraRowWords = mask.rowWordsLCD() - width; + + do { + unsigned w = width; + do { + const uint32_t alphaPixel = *alpha32++; + const uint32_t originalPixel = *device; + *device++ = BlendLCDPixelWithOpaqueColor(alphaPixel, originalPixel, srcColor); + } while (--w != 0); + device = (uint32_t*)((char*)device + devRB); + alpha32 += alphaExtraRowWords; + } while (--height != 0); + + return; + } +#endif + + uint32_t* device = fDevice.getAddr32(x, y); + const uint8_t* alpha = mask.getAddr(x, y); + unsigned maskRB = mask.fRowBytes - width; + unsigned devRB = fDevice.rowBytes() - (width << 2); do { int w = width; do { @@ -298,18 +350,49 @@ void SkARGB32_Black_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) { SkARGB32_BlitBW(fDevice, mask, clip, black); } else { - uint32_t* device = fDevice.getAddr32(clip.fLeft, clip.fTop); - const uint8_t* alpha = mask.getAddr(clip.fLeft, clip.fTop); +#if defined(SK_SUPPORT_LCDTEXT) + const bool lcdMode = mask.fFormat == SkMask::kHorizontalLCD_Format; + const bool verticalLCDMode = mask.fFormat == SkMask::kVerticalLCD_Format; +#endif + + // In LCD mode the masks have either an extra couple of rows or columns on the edges. unsigned width = clip.width(); unsigned height = clip.height(); - unsigned deviceRB = fDevice.rowBytes() - (width << 2); - unsigned maskRB = mask.fRowBytes - width; SkASSERT((int)height > 0); SkASSERT((int)width > 0); - SkASSERT((int)deviceRB >= 0); - SkASSERT((int)maskRB >= 0); +#if defined(SK_SUPPORT_LCDTEXT) + if (lcdMode || verticalLCDMode) { + int widthAdjustment, heightAdjustment; + const uint32_t* alpha32; + uint32_t* device = adjustForSubpixelClip(mask, clip, fDevice, &widthAdjustment, &heightAdjustment, &alpha32); + + width += widthAdjustment; + height += heightAdjustment; + + unsigned deviceRB = fDevice.rowBytes() - (width << 2); + unsigned alphaExtraRowWords = mask.rowWordsLCD() - width; + + do { + unsigned w = width; + do { + const uint32_t alphaPixel = *alpha32++; + const uint32_t originalPixel = *device; + *device++ = BlendLCDPixelWithBlack(alphaPixel, originalPixel); + } while (--w != 0); + device = (uint32_t*)((char*)device + deviceRB); + alpha32 += alphaExtraRowWords; + } while (--height != 0); + + return; + } +#endif + + uint32_t* device = fDevice.getAddr32(clip.fLeft, clip.fTop); + unsigned maskRB = mask.fRowBytes - width; + unsigned deviceRB = fDevice.rowBytes() - (width << 2); + const uint8_t* alpha = mask.getAddr(clip.fLeft, clip.fTop); do { unsigned w = width; do { @@ -482,4 +565,3 @@ void SkARGB32_Shader_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], } } } - diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp index 2413168..23031c5 100644 --- a/src/core/SkCanvas.cpp +++ b/src/core/SkCanvas.cpp @@ -893,20 +893,28 @@ bool SkCanvas::quickReject(const SkRect& rect, EdgeType) const { return true; } - const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(); + if (fMCRec->fMatrix->getType() & SkMatrix::kPerspective_Mask) { + SkRect dst; + fMCRec->fMatrix->mapRect(&dst, rect); + SkIRect idst; + dst.roundOut(&idst); + return !SkIRect::Intersects(idst, fMCRec->fRegion->getBounds()); + } else { + const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(); - // for speed, do the most likely reject compares first - SkScalarCompareType userT = SkScalarToCompareType(rect.fTop); - SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom); - if (userT >= clipR.fBottom || userB <= clipR.fTop) { - return true; + // for speed, do the most likely reject compares first + SkScalarCompareType userT = SkScalarToCompareType(rect.fTop); + SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom); + if (userT >= clipR.fBottom || userB <= clipR.fTop) { + return true; + } + SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft); + SkScalarCompareType userR = SkScalarToCompareType(rect.fRight); + if (userL >= clipR.fRight || userR <= clipR.fLeft) { + return true; + } + return false; } - SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft); - SkScalarCompareType userR = SkScalarToCompareType(rect.fRight); - if (userL >= clipR.fRight || userR <= clipR.fLeft) { - return true; - } - return false; } bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const { diff --git a/src/core/SkEdge.cpp b/src/core/SkEdge.cpp index f790d02..92c78c8 100644 --- a/src/core/SkEdge.cpp +++ b/src/core/SkEdge.cpp @@ -419,8 +419,9 @@ int SkCubicEdge::setCubic(const SkPoint pts[4], const SkIRect* clip, int shift) if (clip) { do { - for (;!this->updateCubic();) - ; + if (!this->updateCubic()) { + return 0; + } } while (!this->intersectsClip(*clip)); this->chopLineWithClip(*clip); return 1; diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp index 8b2a21b..cb78734 100644 --- a/src/core/SkPaint.cpp +++ b/src/core/SkPaint.cpp @@ -57,6 +57,7 @@ SkPaint::SkPaint() fTextAlign = kLeft_Align; fStyle = kFill_Style; fTextEncoding = kUTF8_TextEncoding; + fHinting = kNormal_Hinting; } SkPaint::SkPaint(const SkPaint& src) @@ -144,6 +145,11 @@ void SkPaint::setSubpixelText(bool doSubpixel) this->setFlags(SkSetClearMask(fFlags, doSubpixel, kSubpixelText_Flag)); } +void SkPaint::setLCDRenderText(bool doLCDRender) +{ + this->setFlags(SkSetClearMask(fFlags, doLCDRender, kLCDRenderText_Flag)); +} + void SkPaint::setLinearText(bool doLinearText) { this->setFlags(SkSetClearMask(fFlags, doLinearText, kLinearText_Flag)); @@ -1118,25 +1124,24 @@ static const SkScalar multipliers[] = { SK_Scalar1/24, SK_Scalar1/32 }; static SkMask::Format computeMaskFormat(const SkPaint& paint) { uint32_t flags = paint.getFlags(); - - return (flags & SkPaint::kAntiAlias_Flag) ? SkMask::kA8_Format : SkMask::kBW_Format; -} -static SkScalerContext::Hints computeScalerHints(const SkPaint& paint) -{ - uint32_t flags = paint.getFlags(); - - if (flags & SkPaint::kLinearText_Flag) - return SkScalerContext::kNo_Hints; - else if (flags & SkPaint::kSubpixelText_Flag) - return SkScalerContext::kSubpixel_Hints; - else - return SkScalerContext::kNormal_Hints; + if (flags & SkPaint::kLCDRenderText_Flag) +#if defined(SK_SUPPORT_LCDTEXT) + return SkFontHost::GetSubpixelOrientation() == SkFontHost::kHorizontal_LCDOrientation ? + SkMask::kHorizontalLCD_Format : SkMask::kVerticalLCD_Format; +#else + return SkMask::kA8_Format; +#endif + if (flags & SkPaint::kAntiAlias_Flag) + return SkMask::kA8_Format; + return SkMask::kBW_Format; } -void SkScalerContext::MakeRec(const SkPaint& paint, const SkMatrix* deviceMatrix, Rec* rec) +void SkScalerContext::MakeRec(const SkPaint& paint, + const SkMatrix* deviceMatrix, Rec* rec) { - SkASSERT(deviceMatrix == NULL || (deviceMatrix->getType() & SkMatrix::kPerspective_Mask) == 0); + SkASSERT(deviceMatrix == NULL || + (deviceMatrix->getType() & SkMatrix::kPerspective_Mask) == 0); rec->fFontID = SkTypeface::UniqueID(paint.getTypeface()); rec->fTextSize = paint.getTextSize(); @@ -1194,9 +1199,17 @@ void SkScalerContext::MakeRec(const SkPaint& paint, const SkMatrix* deviceMatrix rec->fStrokeJoin = 0; } - rec->fHints = SkToU8(computeScalerHints(paint)); + rec->fSubpixelPositioning = paint.isSubpixelText(); rec->fMaskFormat = SkToU8(computeMaskFormat(paint)); rec->fFlags = SkToU8(flags); + rec->setHinting(paint.getHinting()); + + /* Allow the fonthost to modify our rec before we use it as a key into the + cache. This way if we're asking for something that they will ignore, + they can modify our rec up front, so we don't create duplicate cache + entries. + */ + SkFontHost::FilterRec(rec); } #define MIN_SIZE_FOR_EFFECT_BUFFER 1024 diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp index 082d2e8..794681c 100644 --- a/src/core/SkPath.cpp +++ b/src/core/SkPath.cpp @@ -986,8 +986,8 @@ SkPath::Verb SkPath::Iter::autoClose(SkPoint pts[2]) { // A special case: if both points are NaN, SkPoint::operation== returns // false, but the iterator expects that they are treated as the same. // (consider SkPoint is a 2-dimension float point). - if (SkScalarIsNaN(fLastPt.fX) && SkScalarIsNaN(fLastPt.fY) && - SkScalarIsNaN(fMoveTo.fX) && SkScalarIsNaN(fMoveTo.fY)) { + if (SkScalarIsNaN(fLastPt.fX) || SkScalarIsNaN(fLastPt.fY) || + SkScalarIsNaN(fMoveTo.fX) || SkScalarIsNaN(fMoveTo.fY)) { return kClose_Verb; } diff --git a/src/core/SkScalerContext.cpp b/src/core/SkScalerContext.cpp index 7d66c2a..eab015a 100644 --- a/src/core/SkScalerContext.cpp +++ b/src/core/SkScalerContext.cpp @@ -16,6 +16,7 @@ */ #include "SkScalerContext.h" +#include "SkColorPriv.h" #include "SkDescriptor.h" #include "SkDraw.h" #include "SkFontHost.h" @@ -45,11 +46,18 @@ void SkGlyph::toMask(SkMask* mask) const { } size_t SkGlyph::computeImageSize() const { - size_t size = this->rowBytes() * fHeight; - if (fMaskFormat == SkMask::k3D_Format) { - size *= 3; + const size_t size = this->rowBytes() * fHeight; + + switch (fMaskFormat) { + case SkMask::kHorizontalLCD_Format: + return SkAlign4(size) + sizeof(uint32_t) * ((fWidth + 2) * fHeight); + case SkMask::kVerticalLCD_Format: + return SkAlign4(size) + sizeof(uint32_t) * (fWidth * (fHeight + 2)); + case SkMask::k3D_Format: + return 3 * size; + default: + return size; } - return size; } void SkGlyph::zeroMetrics() { @@ -63,6 +71,48 @@ void SkGlyph::zeroMetrics() { fLsbDelta = 0; } +void SkGlyph::expandA8ToLCD() const { + SkASSERT(fMaskFormat == SkMask::kHorizontalLCD_Format || + fMaskFormat == SkMask::kVerticalLCD_Format); + +#if defined(SK_SUPPORT_LCDTEXT) + uint8_t* input = reinterpret_cast<uint8_t*>(fImage); + uint32_t* output = reinterpret_cast<uint32_t*>(input + SkAlign4(rowBytes() * fHeight)); + + if (fMaskFormat == SkMask::kHorizontalLCD_Format) { + for (unsigned y = 0; y < fHeight; ++y) { + const uint8_t* inputRow = input; + *output++ = 0; // make the extra column on the left clear + for (unsigned x = 0; x < fWidth; ++x) { + const uint8_t alpha = *inputRow++; + *output++ = SkPackARGB32(alpha, alpha, alpha, alpha); + } + *output++ = 0; + + input += rowBytes(); + } + } else { + const unsigned outputRowBytes = sizeof(uint32_t) * fWidth; + memset(output, 0, outputRowBytes); + output += fWidth; + + for (unsigned y = 0; y < fHeight; ++y) { + const uint8_t* inputRow = input; + for (unsigned x = 0; x < fWidth; ++x) { + const uint8_t alpha = *inputRow++; + *output++ = SkPackARGB32(alpha, alpha, alpha, alpha); + } + + input += rowBytes(); + } + + memset(output, 0, outputRowBytes); + output += fWidth; + } +#else +#endif +} + /////////////////////////////////////////////////////////////////////////////// #ifdef SK_DEBUG @@ -329,6 +379,9 @@ void SkScalerContext::getImage(const SkGlyph& origGlyph) { this->internalGetPath(*glyph, &fillPath, &devPath, &fillToDevMatrix); + const bool lcdMode = fRec.fMaskFormat == SkMask::kHorizontalLCD_Format || + fRec.fMaskFormat == SkMask::kVerticalLCD_Format; + if (fRasterizer) { SkMask mask; @@ -349,7 +402,7 @@ void SkScalerContext::getImage(const SkGlyph& origGlyph) { SkPaint paint; SkDraw draw; - if (SkMask::kA8_Format == fRec.fMaskFormat) { + if (SkMask::kA8_Format == fRec.fMaskFormat || lcdMode) { config = SkBitmap::kA8_Config; paint.setAntiAlias(true); } else { @@ -372,6 +425,9 @@ void SkScalerContext::getImage(const SkGlyph& origGlyph) { draw.fBounder = NULL; draw.drawPath(devPath, paint); } + + if (lcdMode) + glyph->expandA8ToLCD(); } else { this->getGlyphContext(*glyph)->generateImage(*glyph); } diff --git a/src/core/SkScan_Path.cpp b/src/core/SkScan_Path.cpp index ead1b85..211259f 100644 --- a/src/core/SkScan_Path.cpp +++ b/src/core/SkScan_Path.cpp @@ -479,10 +479,9 @@ void sk_fill_path(const SkPath& path, const SkIRect* clipRect, SkBlitter* blitte SkEdge headEdge, tailEdge, *last; SkASSERT(count <= maxCount); - if (count == 0) { + if (count < 2) { return; } - SkASSERT(count > 1); // this returns the first and last edge after they're sorted into a dlink list edge = sort_edges(list, count, &last); diff --git a/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp index b30aba5..dbba1a2 100644 --- a/src/ports/SkFontHost_FreeType.cpp +++ b/src/ports/SkFontHost_FreeType.cpp @@ -15,6 +15,7 @@ ** limitations under the License. */ +#include "SkColorPriv.h" #include "SkScalerContext.h" #include "SkBitmap.h" #include "SkCanvas.h" @@ -32,6 +33,11 @@ #include FT_OUTLINE_H #include FT_SIZES_H #include FT_TRUETYPE_TABLES_H + +#if defined(SK_SUPPORT_LCDTEXT) +#include FT_LCD_FILTER_H +#endif + #ifdef FT_ADVANCES_H #include FT_ADVANCES_H #endif @@ -43,6 +49,7 @@ #include <freetype/ftsizes.h> #include <freetype/tttables.h> #include <freetype/ftadvanc.h> +#include <freetype/ftlcdfil.h> #endif //#define ENABLE_GLYPH_SPEW // for tracing calls @@ -69,6 +76,21 @@ static SkFaceRec* gFaceRecHead; ///////////////////////////////////////////////////////////////////////// +static bool +InitFreetype() { + FT_Error err = FT_Init_FreeType(&gFTLibrary); + if (err) + return false; + +#if defined(SK_SUPPORT_LCDTEXT) + // Setup LCD filtering. This reduces colour fringes for LCD rendered + // glyphs. + err = FT_Library_SetLcdFilter(gFTLibrary, FT_LCD_FILTER_DEFAULT); +#endif + + return true; +} + class SkScalerContext_FreeType : public SkScalerContext { public: SkScalerContext_FreeType(const SkDescriptor* desc); @@ -240,16 +262,26 @@ static void unref_ft_face(FT_Face face) { /////////////////////////////////////////////////////////////////////////// +void SkFontHost::FilterRec(SkScalerContext::Rec* rec) { + SkPaint::Hinting h = rec->getHinting(); + if (SkPaint::kFull_Hinting == h && !rec->isLCD()) { + // collapse full->normaling hinting if we're not doing LCD + h = SkPaint::kNormal_Hinting; + } else if (rec->fSubpixelPositioning && SkPaint::kNo_Hinting != h) { + // to do subpixel, we must have at most slight hinting + h = SkPaint::kSlight_Hinting; + } + rec->setHinting(h); +} + SkScalerContext_FreeType::SkScalerContext_FreeType(const SkDescriptor* desc) : SkScalerContext(desc) { SkAutoMutexAcquire ac(gFTMutex); - FT_Error err; - if (gFTCount == 0) { - err = FT_Init_FreeType(&gFTLibrary); -// SkDEBUGF(("FT_Init_FreeType returned %d\n", err)); - SkASSERT(err == 0); + if (!InitFreetype()) { + sk_throw(); + } } ++gFTCount; @@ -275,7 +307,7 @@ SkScalerContext_FreeType::SkScalerContext_FreeType(const SkDescriptor* desc) SkScalarToFloat(fRec.fPreScaleX), SkScalarToFloat(fRec.fPreSkewX), SkScalarToFloat(fRec.fPost2x2[0][0]), SkScalarToFloat(fRec.fPost2x2[0][1]), SkScalarToFloat(fRec.fPost2x2[1][0]), SkScalarToFloat(fRec.fPost2x2[1][1]), - fRec.fHints, fRec.fMaskFormat, keyString.c_str()); + fRec.getHinting(), fRec.fMaskFormat, keyString.c_str()); #endif // now compute our scale factors @@ -305,42 +337,31 @@ SkScalerContext_FreeType::SkScalerContext_FreeType(const SkDescriptor* desc) // compute the flags we send to Load_Glyph { - uint32_t flags = FT_LOAD_DEFAULT; - uint32_t render_flags = FT_LOAD_TARGET_NORMAL; + FT_Int32 loadFlags = FT_LOAD_DEFAULT; - // we force autohinting at the moment - - switch (fRec.fHints) { - case kNo_Hints: - flags |= FT_LOAD_NO_HINTING; + switch (fRec.getHinting()) { + case SkPaint::kNo_Hinting: + loadFlags = FT_LOAD_NO_HINTING; break; - case kSubpixel_Hints: - flags |= FT_LOAD_FORCE_AUTOHINT; - render_flags = FT_LOAD_TARGET_LIGHT; + case SkPaint::kSlight_Hinting: + loadFlags = FT_LOAD_TARGET_LIGHT; // This implies FORCE_AUTOHINT break; - case kNormal_Hints: -#ifdef ANDROID - // The following line disables the font's hinting tables. For - // Chromium we want font hinting on so that we can generate - // baselines that look at little like Firefox. It's expected that - // Mike Reed will rework this code sometime soon so we don't wish - // to make more extensive changes. - flags |= FT_LOAD_FORCE_AUTOHINT; - /* Switch to light hinting (vertical only) to address some chars - that behaved poorly with NORMAL. In the future we could consider - making this choice exposed at runtime to the caller. - */ - render_flags = FT_LOAD_TARGET_LIGHT; -#endif + case SkPaint::kNormal_Hinting: + loadFlags = FT_LOAD_TARGET_NORMAL; + break; + case SkPaint::kFull_Hinting: + loadFlags = FT_LOAD_TARGET_NORMAL; + if (SkMask::kHorizontalLCD_Format == fRec.fMaskFormat) + loadFlags = FT_LOAD_TARGET_LCD; + else if (SkMask::kVerticalLCD_Format == fRec.fMaskFormat) + loadFlags = FT_LOAD_TARGET_LCD_V; + break; + default: + SkDebugf("---------- UNKNOWN hinting %d\n", fRec.getHinting()); break; } - if (SkMask::kBW_Format == fRec.fMaskFormat) - render_flags = FT_LOAD_TARGET_MONO; - else if (SkMask::kLCD_Format == fRec.fMaskFormat) - render_flags = FT_LOAD_TARGET_LCD; - - fLoadGlyphFlags = flags | render_flags; + fLoadGlyphFlags = loadFlags; } // now create the FT_Size @@ -433,10 +454,12 @@ uint16_t SkScalerContext_FreeType::generateCharToGlyph(SkUnichar uni) { static FT_Pixel_Mode compute_pixel_mode(SkMask::Format format) { switch (format) { + case SkMask::kHorizontalLCD_Format: + case SkMask::kVerticalLCD_Format: + SkASSERT(!"An LCD format should never be passed here"); + return FT_PIXEL_MODE_GRAY; case SkMask::kBW_Format: return FT_PIXEL_MODE_MONO; - case SkMask::kLCD_Format: - return FT_PIXEL_MODE_LCD; case SkMask::kA8_Format: default: return FT_PIXEL_MODE_GRAY; @@ -503,7 +526,7 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) { FT_Outline_Get_CBox(&fFace->glyph->outline, &bbox); - if (kSubpixel_Hints == fRec.fHints) { + if (fRec.fSubpixelPositioning) { int dx = glyph->getSubXFixed() >> 10; int dy = glyph->getSubYFixed() >> 10; // negate dy since freetype-y-goes-up and skia-y-goes-down @@ -536,7 +559,7 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) { goto ERROR; } - if (kNormal_Hints == fRec.fHints) { + if (!fRec.fSubpixelPositioning) { glyph->fAdvanceX = SkFDot6ToFixed(fFace->glyph->advance.x); glyph->fAdvanceY = -SkFDot6ToFixed(fFace->glyph->advance.y); if (fRec.fFlags & kDevKernText_Flag) { @@ -554,6 +577,16 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) { #endif } +#if defined(SK_SUPPORT_LCDTEXT) +namespace skia_freetype_support { +// extern functions from SkFontHost_FreeType_Subpixel +extern void CopyFreetypeBitmapToLCDMask(const SkGlyph& dest, const FT_Bitmap& source); +extern void CopyFreetypeBitmapToVerticalLCDMask(const SkGlyph& dest, const FT_Bitmap& source); +} + +using namespace skia_freetype_support; +#endif + void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph) { SkAutoMutexAcquire ac(gFTMutex); @@ -572,6 +605,9 @@ void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph) { return; } + const bool lcdRenderMode = fRec.fMaskFormat == SkMask::kHorizontalLCD_Format || + fRec.fMaskFormat == SkMask::kVerticalLCD_Format; + switch ( fFace->glyph->format ) { case FT_GLYPH_FORMAT_OUTLINE: { FT_Outline* outline = &fFace->glyph->outline; @@ -579,7 +615,7 @@ void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph) { FT_Bitmap target; int dx = 0, dy = 0; - if (kSubpixel_Hints == fRec.fHints) { + if (fRec.fSubpixelPositioning) { dx = glyph.getSubXFixed() >> 10; dy = glyph.getSubYFixed() >> 10; // negate dy since freetype-y-goes-up and skia-y-goes-down @@ -597,6 +633,23 @@ void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph) { FT_Outline_Translate(outline, dx - ((bbox.xMin + dx) & ~63), dy - ((bbox.yMin + dy) & ~63)); +#if defined(SK_SUPPORT_LCDTEXT) + if (lcdRenderMode) { + // FT_Outline_Get_Bitmap cannot render LCD glyphs. In this case + // we have to call FT_Render_Glyph and memcpy the image out. + const bool isVertical = fRec.fMaskFormat == SkMask::kVerticalLCD_Format; + FT_Render_Mode mode = isVertical ? FT_RENDER_MODE_LCD_V : FT_RENDER_MODE_LCD; + FT_Render_Glyph(fFace->glyph, mode); + + if (isVertical) + CopyFreetypeBitmapToVerticalLCDMask(glyph, fFace->glyph->bitmap); + else + CopyFreetypeBitmapToLCDMask(glyph, fFace->glyph->bitmap); + + break; + } +#endif + target.width = glyph.fWidth; target.rows = glyph.fHeight; target.pitch = glyph.rowBytes(); @@ -652,6 +705,10 @@ void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph) { dst += glyph.rowBytes(); } } + + if (lcdRenderMode) + glyph.expandA8ToLCD(); + } break; default: @@ -905,4 +962,3 @@ SkTypeface::Style find_name_and_style(SkStream* stream, SkString* name) { FT_Done_FreeType(library); return (SkTypeface::Style)style; } - |