aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMike Reed <reed@google.com>2009-07-24 15:53:43 -0400
committerMike Reed <reed@google.com>2009-07-27 12:40:11 -0400
commitaf2616552738d653d5453915d3236e7154b868cd (patch)
tree2adf051e4585ac1afd72b3c91cdd0ed1e0bfb438
parent8db6227fff273d07b62767a9f25dc5a625bd9f63 (diff)
downloadexternal_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.h46
-rw-r--r--include/core/SkMask.h50
-rw-r--r--include/core/SkPaint.h56
-rw-r--r--include/core/SkScalerContext.h41
-rw-r--r--include/core/SkUserConfig.h1
-rw-r--r--src/core/SkBitmapProcState.cpp23
-rw-r--r--src/core/SkBitmapProcState_shaderproc.h27
-rw-r--r--src/core/SkBlitter_ARGB32.cpp104
-rw-r--r--src/core/SkCanvas.cpp32
-rw-r--r--src/core/SkEdge.cpp5
-rw-r--r--src/core/SkPaint.cpp45
-rw-r--r--src/core/SkPath.cpp4
-rw-r--r--src/core/SkScalerContext.cpp66
-rw-r--r--src/core/SkScan_Path.cpp3
-rw-r--r--src/ports/SkFontHost_FreeType.cpp140
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;
}
-