зеркало из https://github.com/mozilla/moz-skia.git
Subpixel glyph rendering support.
This patch adds support for rendering subpixel glyphs (using Freetype). In order to control this rendering see SkPaint::setLCDRenderText in SkPaint.h. To setup the LCD mode, see SkFontHost::SetSubpixelOrientation and SkFontHost::SetSubpixelOrder in SkFontHost.h. This patch also adds more fine grained control over hinting (again, only for Freetype currently). One can now control the hinting with SkPaint::setHinting. git-svn-id: http://skia.googlecode.com/svn/trunk@275 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
Родитель
a380ae4a9a
Коммит
309485b7b5
|
@ -187,6 +187,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_LCDOrientatoin = 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
|
||||
|
|
|
@ -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 == kHorizontanLCD_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.
|
||||
*/
|
||||
const 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);
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -136,34 +136,47 @@ 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 = 0x10,
|
||||
kHintingBit2 = 0x20,
|
||||
};
|
||||
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 >> 4) & 3);
|
||||
}
|
||||
|
||||
void setHinting(SkPaint::Hinting hinting) {
|
||||
fFlags = (fFlags & ~(kHintingBit1 | kHintingBit2)) |
|
||||
(static_cast<int>(hinting) << 4);
|
||||
}
|
||||
};
|
||||
|
||||
SkScalerContext(const SkDescriptor* desc);
|
||||
|
|
|
@ -21,6 +21,21 @@
|
|||
#include "SkUtils.h"
|
||||
#include "SkXfermode.h"
|
||||
|
||||
#if defined(SK_BUILD_SUBPIXEL)
|
||||
namespace skia_blitter_support {
|
||||
// subpixel helper functions from SkBlitter_ARGB32_Subpixel.cpp
|
||||
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 +216,47 @@ 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);
|
||||
uint32_t srcColor = fPMColor;
|
||||
unsigned devRB = fDevice.rowBytes() - (width << 2);
|
||||
unsigned maskRB = mask.fRowBytes - width;
|
||||
#if defined(SK_BUILD_SUBPIXEL)
|
||||
const bool lcdMode = mask.fFormat == SkMask::kHorizontalLCD_Format;
|
||||
const bool verticalLCDMode = mask.fFormat == SkMask::kVerticalLCD_Format;
|
||||
#else
|
||||
const bool lcdMode = false, verticalLCDMode = false;
|
||||
#endif
|
||||
|
||||
// In LCD mode the masks have either an extra couple of rows or columns on the edges.
|
||||
uint32_t* device = fDevice.getAddr32(x - lcdMode, y - verticalLCDMode);
|
||||
uint32_t srcColor = fPMColor;
|
||||
|
||||
#if defined(SK_BUILD_SUBPIXEL)
|
||||
if (lcdMode || verticalLCDMode) {
|
||||
const uint32_t* alpha32 = mask.getAddrLCD(clip.fLeft, clip.fTop);
|
||||
|
||||
if (lcdMode)
|
||||
width += 2; // we have extra columns on the left and right
|
||||
else
|
||||
height += 2; // we have extra rows at the top and bottom
|
||||
|
||||
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
|
||||
|
||||
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 +348,53 @@ 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_BUILD_SUBPIXEL)
|
||||
const bool lcdMode = mask.fFormat == SkMask::kHorizontalLCD_Format;
|
||||
const bool verticalLCDMode = mask.fFormat == SkMask::kVerticalLCD_Format;
|
||||
#else
|
||||
const bool lcdMode = false, verticalLCDMode = false;
|
||||
#endif
|
||||
|
||||
// In LCD mode the masks have either an extra couple of rows or columns on the edges.
|
||||
uint32_t* device = fDevice.getAddr32(clip.fLeft - lcdMode, clip.fTop - verticalLCDMode);
|
||||
unsigned width = clip.width();
|
||||
unsigned height = clip.height();
|
||||
unsigned deviceRB = fDevice.rowBytes() - (width << 2);
|
||||
unsigned maskRB = mask.fRowBytes - width;
|
||||
const uint32_t* alpha32;
|
||||
|
||||
SkASSERT((int)height > 0);
|
||||
SkASSERT((int)width > 0);
|
||||
SkASSERT((int)deviceRB >= 0);
|
||||
SkASSERT((int)maskRB >= 0);
|
||||
|
||||
#if defined(SK_BUILD_SUBPIXEL)
|
||||
if (lcdMode || verticalLCDMode) {
|
||||
const uint32_t* alpha32 = mask.getAddrLCD(clip.fLeft, clip.fTop);
|
||||
if (lcdMode)
|
||||
width += 2; // we have extra columns on the left and right
|
||||
else
|
||||
height += 2; // we have extra rows at the top and bottom
|
||||
|
||||
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
|
||||
|
||||
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 +567,3 @@ void SkARGB32_Shader_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[],
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
/* libs/graphics/sgl/SkBlitter_ARGB32_Subpixel.cpp
|
||||
**
|
||||
** Copyright 2009, The Android Open Source Project
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
** You may obtain a copy of the License at
|
||||
**
|
||||
** http://www.apache.org/licenses/LICENSE-2.0
|
||||
**
|
||||
** Unless required by applicable law or agreed to in writing, software
|
||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
** See the License for the specific language governing permissions and
|
||||
** limitations under the License.
|
||||
*/
|
||||
|
||||
/* LCD blend functions:
|
||||
|
||||
These functions take an alpha pixel of the following form:
|
||||
red, green, blue -> an alpha value for the given colour component.
|
||||
alpha -> the max of the red, green and blue alpha values.
|
||||
|
||||
These alpha pixels result from subpixel renderering. The R/G/B values have
|
||||
already been corrected for RGB/BGR element ordering.
|
||||
|
||||
The alpha pixel is blended with an original pixel and a source colour,
|
||||
resulting in a new pixel value.
|
||||
*/
|
||||
|
||||
#include "SkColorPriv.h"
|
||||
|
||||
namespace skia_blitter_support {
|
||||
|
||||
uint32_t BlendLCDPixelWithColor(const uint32_t alphaPixel, const uint32_t originalPixel,
|
||||
const uint32_t sourcePixel) {
|
||||
unsigned alphaRed = SkAlpha255To256(SkGetPackedR32(alphaPixel));
|
||||
unsigned alphaGreen = SkAlpha255To256(SkGetPackedG32(alphaPixel));
|
||||
unsigned alphaBlue = SkAlpha255To256(SkGetPackedB32(alphaPixel));
|
||||
|
||||
unsigned sourceRed = SkGetPackedR32(sourcePixel);
|
||||
unsigned sourceGreen = SkGetPackedG32(sourcePixel);
|
||||
unsigned sourceBlue = SkGetPackedB32(sourcePixel);
|
||||
unsigned sourceAlpha = SkAlpha255To256(SkGetPackedA32(sourcePixel));
|
||||
|
||||
alphaRed = (alphaRed * sourceAlpha) >> 8;
|
||||
alphaGreen = (alphaGreen * sourceAlpha) >> 8;
|
||||
alphaBlue = (alphaBlue * sourceAlpha) >> 8;
|
||||
unsigned alphaAlpha = SkMax32(SkMax32(alphaRed, alphaBlue), alphaGreen);
|
||||
|
||||
unsigned originalRed = SkGetPackedR32(originalPixel);
|
||||
unsigned originalGreen = SkGetPackedG32(originalPixel);
|
||||
unsigned originalBlue = SkGetPackedB32(originalPixel);
|
||||
unsigned originalAlpha = SkGetPackedA32(originalPixel);
|
||||
|
||||
return SkPackARGB32(SkMin32(255u, alphaAlpha + originalAlpha),
|
||||
((sourceRed * alphaRed) >> 8) + ((originalRed * (256 - alphaRed)) >> 8),
|
||||
((sourceGreen * alphaGreen) >> 8) + ((originalGreen * (256 - alphaGreen)) >> 8),
|
||||
((sourceBlue * alphaBlue) >> 8) + ((originalBlue * (256 - alphaBlue)) >> 8));
|
||||
|
||||
}
|
||||
|
||||
uint32_t BlendLCDPixelWithOpaqueColor(const uint32_t alphaPixel, const uint32_t originalPixel,
|
||||
const uint32_t sourcePixel) {
|
||||
unsigned alphaRed = SkAlpha255To256(SkGetPackedR32(alphaPixel));
|
||||
unsigned alphaGreen = SkAlpha255To256(SkGetPackedG32(alphaPixel));
|
||||
unsigned alphaBlue = SkAlpha255To256(SkGetPackedB32(alphaPixel));
|
||||
unsigned alphaAlpha = SkGetPackedA32(alphaPixel);
|
||||
|
||||
unsigned sourceRed = SkGetPackedR32(sourcePixel);
|
||||
unsigned sourceGreen = SkGetPackedG32(sourcePixel);
|
||||
unsigned sourceBlue = SkGetPackedB32(sourcePixel);
|
||||
|
||||
unsigned originalRed = SkGetPackedR32(originalPixel);
|
||||
unsigned originalGreen = SkGetPackedG32(originalPixel);
|
||||
unsigned originalBlue = SkGetPackedB32(originalPixel);
|
||||
unsigned originalAlpha = SkGetPackedA32(originalPixel);
|
||||
|
||||
return SkPackARGB32(SkMin32(255u, alphaAlpha + originalAlpha),
|
||||
((sourceRed * alphaRed) >> 8) + ((originalRed * (256 - alphaRed)) >> 8),
|
||||
((sourceGreen * alphaGreen) >> 8) + ((originalGreen * (256 - alphaGreen)) >> 8),
|
||||
((sourceBlue * alphaBlue) >> 8) + ((originalBlue * (256 - alphaBlue)) >> 8));
|
||||
}
|
||||
|
||||
uint32_t BlendLCDPixelWithBlack(const uint32_t alphaPixel, const uint32_t originalPixel) {
|
||||
unsigned alphaRed = SkAlpha255To256(SkGetPackedR32(alphaPixel));
|
||||
unsigned alphaGreen = SkAlpha255To256(SkGetPackedG32(alphaPixel));
|
||||
unsigned alphaBlue = SkAlpha255To256(SkGetPackedB32(alphaPixel));
|
||||
unsigned alphaAlpha = SkGetPackedA32(alphaPixel);
|
||||
|
||||
unsigned originalRed = SkGetPackedR32(originalPixel);
|
||||
unsigned originalGreen = SkGetPackedG32(originalPixel);
|
||||
unsigned originalBlue = SkGetPackedB32(originalPixel);
|
||||
unsigned originalAlpha = SkGetPackedA32(originalPixel);
|
||||
|
||||
return SkPackARGB32(SkMin32(255u, alphaAlpha + originalAlpha),
|
||||
(originalRed * (256 - alphaRed)) >> 8,
|
||||
(originalGreen * (256 - alphaGreen)) >> 8,
|
||||
(originalBlue * (256 - alphaBlue)) >> 8);
|
||||
}
|
||||
|
||||
} // namespace skia_blitter_support
|
|
@ -0,0 +1,90 @@
|
|||
/* libs/graphics/sgl/SkFontHost.cpp
|
||||
**
|
||||
** Copyright 2009, The Android Open Source Project
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
** You may obtain a copy of the License at
|
||||
**
|
||||
** http://www.apache.org/licenses/LICENSE-2.0
|
||||
**
|
||||
** Unless required by applicable law or agreed to in writing, software
|
||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
** See the License for the specific language governing permissions and
|
||||
** limitations under the License.
|
||||
*/
|
||||
|
||||
#include "SkFontHost.h"
|
||||
|
||||
static SkFontHost::LCDOrientation gLCDOrientation = SkFontHost::kHorizontal_LCDOrientation;
|
||||
static SkFontHost::LCDOrder gLCDOrder = SkFontHost::kRGB_LCDOrder;
|
||||
|
||||
// static
|
||||
SkFontHost::LCDOrientation SkFontHost::GetSubpixelOrientation()
|
||||
{
|
||||
return gLCDOrientation;
|
||||
}
|
||||
|
||||
// static
|
||||
void SkFontHost::SetSubpixelOrientation(LCDOrientation orientation)
|
||||
{
|
||||
gLCDOrientation = orientation;
|
||||
}
|
||||
|
||||
// static
|
||||
SkFontHost::LCDOrder SkFontHost::GetSubpixelOrder()
|
||||
{
|
||||
return gLCDOrder;
|
||||
}
|
||||
|
||||
// static
|
||||
void SkFontHost::SetSubpixelOrder(LCDOrder order)
|
||||
{
|
||||
gLCDOrder = order;
|
||||
}
|
||||
/* libs/graphics/sgl/SkFontHost.cpp
|
||||
**
|
||||
** Copyright 2009, The Android Open Source Project
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
** You may obtain a copy of the License at
|
||||
**
|
||||
** http://www.apache.org/licenses/LICENSE-2.0
|
||||
**
|
||||
** Unless required by applicable law or agreed to in writing, software
|
||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
** See the License for the specific language governing permissions and
|
||||
** limitations under the License.
|
||||
*/
|
||||
|
||||
#include "SkFontHost.h"
|
||||
|
||||
static SkFontHost::LCDOrientation gLCDOrientation = SkFontHost::kHorizontal_LCDOrientation;
|
||||
static SkFontHost::LCDOrder gLCDOrder = SkFontHost::kRGB_LCDOrder;
|
||||
|
||||
// static
|
||||
SkFontHost::LCDOrientation SkFontHost::GetSubpixelOrientation()
|
||||
{
|
||||
return gLCDOrientation;
|
||||
}
|
||||
|
||||
// static
|
||||
void SkFontHost::SetSubpixelOrientation(LCDOrientation orientation)
|
||||
{
|
||||
gLCDOrientation = orientation;
|
||||
}
|
||||
|
||||
// static
|
||||
SkFontHost::LCDOrder SkFontHost::GetSubpixelOrder()
|
||||
{
|
||||
return gLCDOrder;
|
||||
}
|
||||
|
||||
// static
|
||||
void SkFontHost::SetSubpixelOrder(LCDOrder order)
|
||||
{
|
||||
gLCDOrder = order;
|
||||
}
|
|
@ -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,20 +1124,17 @@ 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_BUILD_SUBPIXEL)
|
||||
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)
|
||||
|
@ -1194,9 +1197,10 @@ 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());
|
||||
}
|
||||
|
||||
#define MIN_SIZE_FOR_EFFECT_BUFFER 1024
|
||||
|
|
|
@ -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_BUILD_SUBPIXEL)
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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_BUILD_SUBPIXEL)
|
||||
#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_BUILD_SUBPIXEL)
|
||||
// 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);
|
||||
|
@ -247,9 +269,8 @@ SkScalerContext_FreeType::SkScalerContext_FreeType(const SkDescriptor* desc)
|
|||
FT_Error err;
|
||||
|
||||
if (gFTCount == 0) {
|
||||
err = FT_Init_FreeType(&gFTLibrary);
|
||||
// SkDEBUGF(("FT_Init_FreeType returned %d\n", err));
|
||||
SkASSERT(err == 0);
|
||||
const bool success = InitFreetype();
|
||||
SkASSERT(success);
|
||||
}
|
||||
++gFTCount;
|
||||
|
||||
|
@ -275,7 +296,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 +326,27 @@ 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 hintingFlags = 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:
|
||||
hintingFlags = FT_LOAD_NO_HINTING;
|
||||
break;
|
||||
case kSubpixel_Hints:
|
||||
flags |= FT_LOAD_FORCE_AUTOHINT;
|
||||
render_flags = FT_LOAD_TARGET_LIGHT;
|
||||
case SkPaint::kSlight_Hinting:
|
||||
hintingFlags = 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:
|
||||
hintingFlags |= FT_LOAD_TARGET_NORMAL;
|
||||
break;
|
||||
case SkPaint::kFull_Hinting:
|
||||
if (SkMask::kHorizontalLCD_Format == fRec.fMaskFormat)
|
||||
hintingFlags = FT_LOAD_TARGET_LCD;
|
||||
else if (SkMask::kVerticalLCD_Format == fRec.fMaskFormat)
|
||||
hintingFlags = FT_LOAD_TARGET_LCD_V;
|
||||
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 = hintingFlags;
|
||||
}
|
||||
|
||||
// now create the FT_Size
|
||||
|
@ -433,10 +439,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 +511,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 +544,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 +562,16 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) {
|
|||
#endif
|
||||
}
|
||||
|
||||
#if defined(SK_BUILD_SUBPIXEL)
|
||||
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 +590,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 +600,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 +618,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_BUILD_SUBPIXEL)
|
||||
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 +690,10 @@ void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph) {
|
|||
dst += glyph.rowBytes();
|
||||
}
|
||||
}
|
||||
|
||||
if (lcdRenderMode)
|
||||
glyph.expandA8ToLCD();
|
||||
|
||||
} break;
|
||||
|
||||
default:
|
||||
|
@ -858,7 +900,7 @@ SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
|
|||
*/
|
||||
SkTypeface::Style find_name_and_style(SkStream* stream, SkString* name) {
|
||||
FT_Library library;
|
||||
if (FT_Init_FreeType(&library)) {
|
||||
if (!InitFreetype()) {
|
||||
name->set(NULL);
|
||||
return SkTypeface::kNormal;
|
||||
}
|
||||
|
@ -905,4 +947,3 @@ SkTypeface::Style find_name_and_style(SkStream* stream, SkString* name) {
|
|||
FT_Done_FreeType(library);
|
||||
return (SkTypeface::Style)style;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
/* libs/graphics/ports/SkFontHost_FreeType_Subpixel.cpp
|
||||
**
|
||||
** Copyright 2009, The Android Open Source Project
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
** You may obtain a copy of the License at
|
||||
**
|
||||
** http://www.apache.org/licenses/LICENSE-2.0
|
||||
**
|
||||
** Unless required by applicable law or agreed to in writing, software
|
||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
** See the License for the specific language governing permissions and
|
||||
** limitations under the License.
|
||||
*/
|
||||
|
||||
/* This file contains functions for converting Freetype's subpixel output
|
||||
formats into the format used by SkMask for subpixel masks. See the comments
|
||||
in SkMask.h for details on the format.
|
||||
*/
|
||||
|
||||
#include "SkColorPriv.h"
|
||||
#include "SkFontHost.h"
|
||||
#include "SkMask.h"
|
||||
#include "SkScalerContext.h"
|
||||
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
#if 0
|
||||
// Also include the files by name for build tools which require this.
|
||||
#include <freetype/freetype.h>
|
||||
#endif
|
||||
|
||||
namespace skia_freetype_support {
|
||||
|
||||
void CopyFreetypeBitmapToLCDMask(const SkGlyph& dest, const FT_Bitmap& source)
|
||||
{
|
||||
// |source| has three alpha values per pixel and has an extra column at the
|
||||
// left and right edges.
|
||||
|
||||
// ----- <--- a single pixel in the output
|
||||
// source .oOo.. |.oO|o..
|
||||
// .OOO.. -----
|
||||
// .oOo.. --> .OO O..
|
||||
// .oO o..
|
||||
|
||||
uint8_t* output = reinterpret_cast<uint8_t*>(dest.fImage);
|
||||
const unsigned outputPitch = SkAlign4((source.width / 3) - 2);
|
||||
const uint8_t* input = source.buffer;
|
||||
|
||||
// First we calculate the A8 mask.
|
||||
for (int y = 0; y < source.rows; ++y) {
|
||||
const uint8_t* inputRow = input;
|
||||
uint8_t* outputRow = output;
|
||||
inputRow += 3; // skip the extra column on the left
|
||||
for (int x = 3; x < source.width - 3; x += 3) {
|
||||
const uint8_t averageAlpha = (static_cast<unsigned>(inputRow[0]) + inputRow[1] + inputRow[2] + 1) / 3;
|
||||
*outputRow++ = averageAlpha;
|
||||
inputRow += 3;
|
||||
}
|
||||
|
||||
input += source.pitch;
|
||||
output += outputPitch;
|
||||
}
|
||||
|
||||
// Align the 32-bit plane on a word boundary
|
||||
uint32_t* output32 = (uint32_t*) SkAlign4((uintptr_t) output);
|
||||
|
||||
// Now we build the 32-bit alpha mask and RGB order correct.
|
||||
const int isBGR = SkFontHost::GetSubpixelOrder() == SkFontHost::kBGR_LCDOrder;
|
||||
input = source.buffer;
|
||||
|
||||
for (int y = 0; y < source.rows; ++y) {
|
||||
const uint8_t* inputRow = input;
|
||||
for (int x = 0; x < source.width; x += 3) {
|
||||
const uint8_t alphaRed = isBGR ? inputRow[2] : inputRow[0];
|
||||
const uint8_t alphaGreen = inputRow[1];
|
||||
const uint8_t alphaBlue = isBGR ? inputRow[0] : inputRow[2];
|
||||
const uint8_t maxAlpha = SkMax32(alphaRed, SkMax32(alphaGreen, alphaBlue));
|
||||
*output32++ = SkPackARGB32(maxAlpha, alphaRed, alphaGreen, alphaBlue);
|
||||
|
||||
inputRow += 3;
|
||||
}
|
||||
|
||||
input += source.pitch;
|
||||
}
|
||||
}
|
||||
|
||||
void CopyFreetypeBitmapToVerticalLCDMask(const SkGlyph& dest, const FT_Bitmap& source)
|
||||
{
|
||||
// |source| has three times as many rows as normal, and an extra triple on the
|
||||
// top and bottom.
|
||||
|
||||
// source .oOo.. |.|oOo..
|
||||
// .OOO.. --> |.|OOO..
|
||||
// .oOo.. |.|oOo..
|
||||
// ^
|
||||
// |-------- A single pixel in the output
|
||||
|
||||
uint8_t* output = reinterpret_cast<uint8_t*>(dest.fImage);
|
||||
const unsigned outputPitch = SkAlign4(source.rows);
|
||||
const uint8_t* input = source.buffer;
|
||||
|
||||
// First we calculate the A8 mask.
|
||||
input += 3 * source.pitch; // skip the extra at the beginning
|
||||
for (int y = 3; y < source.rows - 3; y += 3) {
|
||||
const uint8_t* inputRow = input;
|
||||
uint8_t* outputRow = output;
|
||||
for (int x = 0; x < source.width; ++x) {
|
||||
const uint8_t averageAlpha = (static_cast<unsigned>(*inputRow) + inputRow[source.pitch] + inputRow[source.pitch * 2] + 1) / 3;
|
||||
*outputRow++ = averageAlpha;
|
||||
inputRow++;
|
||||
}
|
||||
|
||||
input += source.pitch * 3;
|
||||
output += outputPitch;
|
||||
}
|
||||
|
||||
// Align the 32-bit plane on a word boundary
|
||||
uint32_t* output32 = (uint32_t*) SkAlign4((uintptr_t) output);
|
||||
|
||||
// Now we build the 32-bit alpha mask and RGB order correct.
|
||||
const int isBGR = SkFontHost::GetSubpixelOrder() == SkFontHost::kBGR_LCDOrder;
|
||||
input = source.buffer;
|
||||
|
||||
for (int y = 0; y < source.rows; y += 3) {
|
||||
const uint8_t* inputRow = input;
|
||||
for (int x = 0; x < source.width; ++x) {
|
||||
const uint8_t alphaRed = isBGR ? inputRow[source.pitch * 2] : inputRow[0];
|
||||
const uint8_t alphaGreen = inputRow[source.pitch];
|
||||
const uint8_t alphaBlue = isBGR ? inputRow[0] : inputRow[2 * source.pitch];
|
||||
const uint8_t maxAlpha = SkMax32(alphaRed, SkMax32(alphaGreen, alphaBlue));
|
||||
*output32++ = SkPackARGB32(maxAlpha, alphaRed, alphaGreen, alphaBlue);
|
||||
inputRow++;
|
||||
}
|
||||
|
||||
input += source.pitch * 3;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace skia_freetype_support
|
Загрузка…
Ссылка в новой задаче