diff options
author | Android Chromium Automerger <chromium-automerger@android> | 2013-12-11 13:32:27 +0000 |
---|---|---|
committer | Android Chromium Automerger <chromium-automerger@android> | 2013-12-11 13:32:27 +0000 |
commit | 24df3e66d89fff6e91ba76a9fe1a4f1e7c413402 (patch) | |
tree | 3c0229f3d6163ee2f43fb32a886cb15deb58227d | |
parent | 190df0154be02c5183f402e619b783580c73f258 (diff) | |
parent | 6168ea7d412448c88169a565f40bf86379d4bf9a (diff) | |
download | src-24df3e66d89fff6e91ba76a9fe1a4f1e7c413402.tar.gz |
Merge third_party/skia/src from https://chromium.googlesource.com/external/skia/src.git at 6168ea7d412448c88169a565f40bf86379d4bf9a
This commit was generated by merge_from_chromium.py.
Change-Id: I9caf28ca55c574145162509dd4edc7bca30f8774
-rw-r--r-- | core/SkBitmapProcShader.cpp | 2 | ||||
-rw-r--r-- | core/SkScaledImageCache.cpp | 4 | ||||
-rw-r--r-- | gpu/SkGpuDevice.cpp | 4 | ||||
-rw-r--r-- | gpu/effects/GrBicubicEffect.cpp | 17 | ||||
-rw-r--r-- | gpu/effects/GrBicubicEffect.h | 44 | ||||
-rw-r--r-- | image/SkImagePriv.cpp | 4 | ||||
-rw-r--r-- | images/SkDecodingImageGenerator.cpp | 7 | ||||
-rw-r--r-- | lazy/SkDiscardablePixelRef.cpp | 15 | ||||
-rw-r--r-- | lazy/SkDiscardablePixelRef.h | 37 | ||||
-rw-r--r-- | ports/SkDiscardableMemory_ashmem.cpp | 114 | ||||
-rw-r--r-- | ports/SkFontConfigInterface_direct.cpp | 2 | ||||
-rw-r--r-- | ports/SkFontHost_FreeType.cpp | 334 | ||||
-rw-r--r-- | ports/SkFontHost_FreeType_common.cpp | 484 | ||||
-rwxr-xr-x | ports/SkFontHost_mac.cpp | 8 | ||||
-rw-r--r-- | utils/android/ashmem.h | 6 |
15 files changed, 778 insertions, 304 deletions
diff --git a/core/SkBitmapProcShader.cpp b/core/SkBitmapProcShader.cpp index fcfcdbf0..bb161192 100644 --- a/core/SkBitmapProcShader.cpp +++ b/core/SkBitmapProcShader.cpp @@ -419,7 +419,7 @@ GrEffectRef* SkBitmapProcShader::asNewEffect(GrContext* context, const SkPaint& GrEffectRef* effect = NULL; if (paintFilterLevel == SkPaint::kHigh_FilterLevel) { - effect = GrBicubicEffect::Create(texture, matrix, params); + effect = GrBicubicEffect::Create(texture, matrix, tm); } else { effect = GrSimpleTextureEffect::Create(texture, matrix, params); } diff --git a/core/SkScaledImageCache.cpp b/core/SkScaledImageCache.cpp index 25e29c2f..5b166887 100644 --- a/core/SkScaledImageCache.cpp +++ b/core/SkScaledImageCache.cpp @@ -257,7 +257,7 @@ public: } virtual bool allocPixelRef(SkBitmap*, SkColorTable*) SK_OVERRIDE; - + private: SkScaledImageCache::DiscardableFactory fFactory; }; @@ -509,7 +509,7 @@ void SkScaledImageCache::purgeAsNeeded() { size_t bytesUsed = fBytesUsed; int countUsed = fCount; - + Rec* rec = fTail; while (rec) { if (bytesUsed < byteLimit && countUsed < countLimit) { diff --git a/gpu/SkGpuDevice.cpp b/gpu/SkGpuDevice.cpp index f45572bb..ce02f2c5 100644 --- a/gpu/SkGpuDevice.cpp +++ b/gpu/SkGpuDevice.cpp @@ -1426,7 +1426,9 @@ void SkGpuDevice::internalDrawBitmap(const SkBitmap& bitmap, GrTextureDomain::kClamp_Mode, params.filterMode())); } else if (bicubic) { - effect.reset(GrBicubicEffect::Create(texture, SkMatrix::I(), params)); + SkASSERT(GrTextureParams::kNone_FilterMode == params.filterMode()); + SkShader::TileMode tileModes[2] = { params.getTileModeX(), params.getTileModeY() }; + effect.reset(GrBicubicEffect::Create(texture, SkMatrix::I(), tileModes)); } else { effect.reset(GrSimpleTextureEffect::Create(texture, SkMatrix::I(), params)); } diff --git a/gpu/effects/GrBicubicEffect.cpp b/gpu/effects/GrBicubicEffect.cpp index cd013cbc..a6e08f20 100644 --- a/gpu/effects/GrBicubicEffect.cpp +++ b/gpu/effects/GrBicubicEffect.cpp @@ -106,23 +106,10 @@ void GrGLBicubicEffect::setData(const GrGLUniformManager& uman, } GrBicubicEffect::GrBicubicEffect(GrTexture* texture, - const SkScalar coefficients[16]) - : INHERITED(texture, MakeDivByTextureWHMatrix(texture)) { - for (int y = 0; y < 4; y++) { - for (int x = 0; x < 4; x++) { - // Convert from row-major scalars to column-major floats. - fCoefficients[x * 4 + y] = SkScalarToFloat(coefficients[y * 4 + x]); - } - } - this->setWillNotUseInputColor(); -} - -GrBicubicEffect::GrBicubicEffect(GrTexture* texture, const SkScalar coefficients[16], const SkMatrix &matrix, - const GrTextureParams ¶ms, - GrCoordSet coordSet) - : INHERITED(texture, matrix, params, coordSet) { + const SkShader::TileMode tileModes[2]) + : INHERITED(texture, matrix, GrTextureParams(tileModes, GrTextureParams::kNone_FilterMode)) { for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { // Convert from row-major scalars to column-major floats. diff --git a/gpu/effects/GrBicubicEffect.h b/gpu/effects/GrBicubicEffect.h index d8c431a4..85bec771 100644 --- a/gpu/effects/GrBicubicEffect.h +++ b/gpu/effects/GrBicubicEffect.h @@ -31,34 +31,44 @@ public: virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE; virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE; - static GrEffectRef* Create(GrTexture* tex, const SkScalar coefficients[16]) { - AutoEffectUnref effect(SkNEW_ARGS(GrBicubicEffect, (tex, coefficients))); - return CreateEffectRef(effect); + /** + * Create a simple Mitchell filter effect. + */ + static GrEffectRef* Create(GrTexture* tex) { + return Create(tex, gMitchellCoefficients); } - static GrEffectRef* Create(GrTexture* tex, const SkScalar coefficients[16], - const SkMatrix& matrix, - const GrTextureParams& p, - GrCoordSet coordSet = kLocal_GrCoordSet) { - AutoEffectUnref effect(SkNEW_ARGS(GrBicubicEffect, (tex, coefficients, matrix, p, coordSet))); - return CreateEffectRef(effect); + /** + * Create a simple filter effect with custom bicubic coefficients. + */ + static GrEffectRef* Create(GrTexture* tex, const SkScalar coefficients[16]) { + const SkShader::TileMode tm[] = { SkShader::kClamp_TileMode, SkShader::kClamp_TileMode }; + return Create(tex, coefficients, MakeDivByTextureWHMatrix(tex), tm); } - static GrEffectRef* Create(GrTexture* tex) { - return Create(tex, gMitchellCoefficients); + /** + * Create a Mitchell filter effect with specified texture matrix and x/y tile modes. + */ + static GrEffectRef* Create(GrTexture* tex, + const SkMatrix& matrix, + SkShader::TileMode tileModes[2]) { + return Create(tex, gMitchellCoefficients, matrix, tileModes); } - static GrEffectRef* Create(GrTexture* tex, + /** + * The most general Create method. This allows specification of the bicubic coefficients, the + * texture matrix, and the x/y tilemodes. + */ + static GrEffectRef* Create(GrTexture* tex, const SkScalar coefficients[16], const SkMatrix& matrix, - const GrTextureParams& p, - GrCoordSet coordSet = kLocal_GrCoordSet) { - return Create(tex, gMitchellCoefficients, matrix, p, coordSet); + const SkShader::TileMode tileModes[2]) { + AutoEffectUnref effect(SkNEW_ARGS(GrBicubicEffect, (tex, coefficients, matrix, tileModes))); + return CreateEffectRef(effect); } private: - GrBicubicEffect(GrTexture*, const SkScalar coefficients[16]); GrBicubicEffect(GrTexture*, const SkScalar coefficients[16], - const SkMatrix &matrix, const GrTextureParams &p, GrCoordSet coordSet); + const SkMatrix &matrix, const SkShader::TileMode tileModes[2]); virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE; float fCoefficients[16]; diff --git a/image/SkImagePriv.cpp b/image/SkImagePriv.cpp index 1e24c48c..976a5b33 100644 --- a/image/SkImagePriv.cpp +++ b/image/SkImagePriv.cpp @@ -16,10 +16,10 @@ SkBitmap::Config SkImageInfoToBitmapConfig(const SkImageInfo& info) { case kARGB_4444_SkColorType: return SkBitmap::kARGB_4444_Config; - + case kRGB_565_SkColorType: return SkBitmap::kRGB_565_Config; - + case kPMColor_SkColorType: return SkBitmap::kARGB_8888_Config; diff --git a/images/SkDecodingImageGenerator.cpp b/images/SkDecodingImageGenerator.cpp index e49f9a4f..a833c636 100644 --- a/images/SkDecodingImageGenerator.cpp +++ b/images/SkDecodingImageGenerator.cpp @@ -7,8 +7,8 @@ #include "SkDecodingImageGenerator.h" #include "SkData.h" -#include "SkDiscardablePixelRef.h" #include "SkImageDecoder.h" +#include "SkImageGenerator.h" #include "SkImagePriv.h" #include "SkStream.h" @@ -191,7 +191,7 @@ bool SkDecodingImageGenerator::Install(SkData* data, SkBitmap* dst, SkASSERT(data != NULL); SkASSERT(dst != NULL); SkImageGenerator* gen(SkNEW_ARGS(SkDecodingImageGenerator, (data))); - return SkDiscardablePixelRef::Install(gen, dst, factory); + return SkInstallDiscardablePixelRef(gen, dst, factory); } bool SkDecodingImageGenerator::Install(SkStreamRewindable* stream, @@ -204,6 +204,5 @@ bool SkDecodingImageGenerator::Install(SkStreamRewindable* stream, return false; } SkImageGenerator* gen(SkNEW_ARGS(SkDecodingImageGenerator, (stream))); - return SkDiscardablePixelRef::Install(gen, dst, factory); + return SkInstallDiscardablePixelRef(gen, dst, factory); } - diff --git a/lazy/SkDiscardablePixelRef.cpp b/lazy/SkDiscardablePixelRef.cpp index e614db37..6a9507c8 100644 --- a/lazy/SkDiscardablePixelRef.cpp +++ b/lazy/SkDiscardablePixelRef.cpp @@ -7,6 +7,7 @@ #include "SkDiscardablePixelRef.h" #include "SkDiscardableMemory.h" +#include "SkImageGenerator.h" SkDiscardablePixelRef::SkDiscardablePixelRef(SkImageGenerator* generator, const SkImageInfo& info, @@ -62,18 +63,22 @@ void SkDiscardablePixelRef::onUnlockPixels() { } } -bool SkDiscardablePixelRef::Install(SkImageGenerator* generator, - SkBitmap* dst, - SkDiscardableMemory::Factory* factory) { +bool SkInstallDiscardablePixelRef(SkImageGenerator* generator, + SkBitmap* dst, + SkDiscardableMemory::Factory* factory) { SkImageInfo info; SkASSERT(generator != NULL); if ((NULL == generator) || (!generator->getInfo(&info)) - || (!dst->setConfig(info, 0)) - || (0 == dst->getSize())) { // dst->getSize=0 Probably a bad config + || (!dst->setConfig(info, 0))) { SkDELETE(generator); return false; } + SkASSERT(dst->config() != SkBitmap::kNo_Config); + if (dst->empty()) { // Use a normal pixelref. + SkDELETE(generator); // Do not need this anymore. + return dst->allocPixels(NULL, NULL); + } SkAutoTUnref<SkDiscardablePixelRef> ref(SkNEW_ARGS(SkDiscardablePixelRef, (generator, info, dst->getSize(), diff --git a/lazy/SkDiscardablePixelRef.h b/lazy/SkDiscardablePixelRef.h index 78dcd667..44c6df96 100644 --- a/lazy/SkDiscardablePixelRef.h +++ b/lazy/SkDiscardablePixelRef.h @@ -9,43 +9,12 @@ #define SkDiscardablePixelRef_DEFINED #include "SkDiscardableMemory.h" -#include "SkPixelRef.h" #include "SkImageGenerator.h" #include "SkImageInfo.h" - -/** - * An interface that allows a purgable PixelRef to re-decode an image. - */ - -typedef SkDiscardableMemory* (*SkDiscardableMemoryFactory)(size_t bytes); - +#include "SkPixelRef.h" class SkDiscardablePixelRef : public SkPixelRef { public: - /** - * Takes ownership of SkImageGenerator. If this method fails for - * whatever reason, it will return false and immediatetely delete - * the generator. If it succeeds, it will modify destination - * bitmap. - * - * If Install fails or when the SkDiscardablePixelRef that is - * installed into destination is destroyed, it will call - * SkDELETE() on the generator. Therefore, generator should be - * allocated with SkNEW() or SkNEW_ARGS(). - * - * @param destination Upon success, this bitmap will be - * configured and have a pixelref installed. - * - * @param factory If not NULL, this object will be used as a - * source of discardable memory when decoding. If NULL, then - * SkDiscardableMemory::Create() will be called. - * - * @return true iff successful. - */ - static bool Install(SkImageGenerator* generator, - SkBitmap* destination, - SkDiscardableMemory::Factory* factory = NULL); - SK_DECLARE_UNFLATTENABLE_OBJECT() protected: @@ -75,5 +44,9 @@ private: size_t size, size_t rowBytes, SkDiscardableMemory::Factory* factory); + friend bool SkInstallDiscardablePixelRef(SkImageGenerator*, + SkBitmap*, + SkDiscardableMemory::Factory*); + typedef SkPixelRef INHERITED; }; #endif // SkDiscardablePixelRef_DEFINED diff --git a/ports/SkDiscardableMemory_ashmem.cpp b/ports/SkDiscardableMemory_ashmem.cpp new file mode 100644 index 00000000..6f8684e3 --- /dev/null +++ b/ports/SkDiscardableMemory_ashmem.cpp @@ -0,0 +1,114 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <unistd.h> +#include <sys/mman.h> +#include "SkDiscardableMemory.h" +#include "SkTypes.h" +#include "android/ashmem.h" + +//////////////////////////////////////////////////////////////////////////////// +namespace { +/** + * DiscardableMemory implementation that uses the Android kernel's + * ashmem (Android shared memory). + */ +class SkAshmemDiscardableMemory : public SkDiscardableMemory { +public: + SkAshmemDiscardableMemory(int fd, void* address, size_t size); + virtual ~SkAshmemDiscardableMemory(); + virtual bool lock() SK_OVERRIDE; + virtual void* data() SK_OVERRIDE; + virtual void unlock() SK_OVERRIDE; +private: + bool fLocked; + int fFd; + void* fMemory; + const size_t fSize; +}; + +SkAshmemDiscardableMemory::SkAshmemDiscardableMemory(int fd, + void* address, + size_t size) + : fLocked(true) // Ashmem pages are pinned by default. + , fFd(fd) + , fMemory(address) + , fSize(size) { + SkASSERT(fFd >= 0); + SkASSERT(fMemory != NULL); + SkASSERT(fSize > 0); +} + +SkAshmemDiscardableMemory::~SkAshmemDiscardableMemory() { + SkASSERT(!fLocked); + if (NULL != fMemory) { + munmap(fMemory, fSize); + } + if (fFd != -1) { + close(fFd); + } +} + +bool SkAshmemDiscardableMemory::lock() { + SkASSERT(!fLocked); + if (-1 == fFd) { + fLocked = false; + return false; + } + SkASSERT(fMemory != NULL); + if (fLocked || (ASHMEM_NOT_PURGED == ashmem_pin_region(fFd, 0, 0))) { + fLocked = true; + return true; + } else { + munmap(fMemory, fSize); + fMemory = NULL; + + close(fFd); + fFd = -1; + fLocked = false; + return false; + } +} + +void* SkAshmemDiscardableMemory::data() { + SkASSERT(fLocked); + return fLocked ? fMemory : NULL; +} + +void SkAshmemDiscardableMemory::unlock() { + SkASSERT(fLocked); + if (fLocked && (fFd != -1)) { + ashmem_unpin_region(fFd, 0, 0); + } + fLocked = false; +} +} // namespace +//////////////////////////////////////////////////////////////////////////////// + +SkDiscardableMemory* SkDiscardableMemory::Create(size_t bytes) { + // ashmem likes lengths on page boundaries. + const size_t mask = getpagesize() - 1; + size_t size = (bytes + mask) & ~mask; + + static const char name[] = "Skia_Ashmem_Discardable_Memory"; + int fd = ashmem_create_region(name, size); + if (fd < 0) { + return NULL; + } + if (0 != ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE)) { + close(fd); + return NULL; + } + void* addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); + if ((MAP_FAILED == addr) || (NULL == addr)) { + close(fd); + return NULL; + } + + return SkNEW_ARGS(SkAshmemDiscardableMemory, (fd, addr, size)); +} + diff --git a/ports/SkFontConfigInterface_direct.cpp b/ports/SkFontConfigInterface_direct.cpp index f1ac7342..13993f10 100644 --- a/ports/SkFontConfigInterface_direct.cpp +++ b/ports/SkFontConfigInterface_direct.cpp @@ -330,11 +330,13 @@ bool IsFallbackFontAllowed(const std::string& family) { } static bool valid_pattern(FcPattern* pattern) { +#ifdef SK_FONT_CONFIG_ONLY_ALLOW_SCALABLE_FONTS FcBool is_scalable; if (FcPatternGetBool(pattern, FC_SCALABLE, 0, &is_scalable) != FcResultMatch || !is_scalable) { return false; } +#endif // fontconfig can also return fonts which are unreadable const char* c_filename = get_name(pattern, FC_FILE); diff --git a/ports/SkFontHost_FreeType.cpp b/ports/SkFontHost_FreeType.cpp index fd87a66d..745a58a9 100644 --- a/ports/SkFontHost_FreeType.cpp +++ b/ports/SkFontHost_FreeType.cpp @@ -58,6 +58,21 @@ #include <freetype/ftsynth.h> #endif +// FT_LOAD_COLOR and the corresponding FT_Pixel_Mode::FT_PIXEL_MODE_BGRA +// were introduced in FreeType 2.5.0. +// The following may be removed once FreeType 2.5.0 is required to build. +#ifndef FT_LOAD_COLOR +# define FT_LOAD_COLOR ( 1L << 20 ) +# define FT_PIXEL_MODE_BGRA 7 +#endif + +// FT_HAS_COLOR and the corresponding FT_FACE_FLAG_COLOR +// were introduced in FreeType 2.5.1 +// The following may be removed once FreeType 2.5.1 is required to build. +#ifndef FT_HAS_COLOR +# define FT_HAS_COLOR(face) false +#endif + //#define ENABLE_GLYPH_SPEW // for tracing calls //#define DUMP_STRIKE_CREATION @@ -184,6 +199,7 @@ private: SkFaceRec* fFaceRec; FT_Face fFace; // reference to shared face in gFaceRecHead FT_Size fFTSize; // our own copy + FT_Int fStrikeIndex; SkFixed fScaleX, fScaleY; FT_Matrix fMatrix22; uint32_t fLoadGlyphFlags; @@ -751,6 +767,50 @@ bool SkTypeface_FreeType::onGetKerningPairAdjustments(const uint16_t glyphs[], return true; } +static FT_Int chooseBitmapStrike(FT_Face face, SkFixed scaleY) { + // early out if face is bad + if (face == NULL) { + SkDEBUGF(("chooseBitmapStrike aborted due to NULL face\n")); + return -1; + } + // determine target ppem + FT_Pos targetPPEM = SkFixedToFDot6(scaleY); + // find a bitmap strike equal to or just larger than the requested size + FT_Int chosenStrikeIndex = -1; + FT_Pos chosenPPEM = 0; + for (FT_Int strikeIndex = 0; strikeIndex < face->num_fixed_sizes; ++strikeIndex) { + FT_Pos thisPPEM = face->available_sizes[strikeIndex].y_ppem; + if (thisPPEM == targetPPEM) { + // exact match - our search stops here + chosenPPEM = thisPPEM; + chosenStrikeIndex = strikeIndex; + break; + } else if (chosenPPEM < targetPPEM) { + // attempt to increase chosenPPEM + if (thisPPEM > chosenPPEM) { + chosenPPEM = thisPPEM; + chosenStrikeIndex = strikeIndex; + } + } else { + // attempt to decrease chosenPPEM, but not below targetPPEM + if (thisPPEM < chosenPPEM && thisPPEM > targetPPEM) { + chosenPPEM = thisPPEM; + chosenStrikeIndex = strikeIndex; + } + } + } + if (chosenStrikeIndex != -1) { + // use the chosen strike + FT_Error err = FT_Select_Size(face, chosenStrikeIndex); + if (err != 0) { + SkDEBUGF(("FT_Select_Size(%s, %d) returned 0x%x\n", face->family_name, + chosenStrikeIndex, err)); + chosenStrikeIndex = -1; + } + } + return chosenStrikeIndex; +} + SkScalerContext_FreeType::SkScalerContext_FreeType(SkTypeface* typeface, const SkDescriptor* desc) : SkScalerContext_FreeType_Base(typeface, desc) { @@ -764,6 +824,7 @@ SkScalerContext_FreeType::SkScalerContext_FreeType(SkTypeface* typeface, ++gFTCount; // load the font file + fStrikeIndex = -1; fFTSize = NULL; fFace = NULL; fFaceRec = ref_ft_face(typeface); @@ -823,9 +884,9 @@ SkScalerContext_FreeType::SkScalerContext_FreeType(SkTypeface* typeface, fLCDIsVert = SkToBool(fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag); // compute the flags we send to Load_Glyph + bool linearMetrics = SkToBool(fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag); { FT_Int32 loadFlags = FT_LOAD_DEFAULT; - bool linearMetrics = SkToBool(fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag); if (SkMask::kBW_Format == fRec.fMaskFormat) { // See http://code.google.com/p/chromium/issues/detail?id=43252#c24 @@ -883,42 +944,57 @@ SkScalerContext_FreeType::SkScalerContext_FreeType(SkTypeface* typeface, loadFlags |= FT_LOAD_VERTICAL_LAYOUT; } + loadFlags |= FT_LOAD_COLOR; + fLoadGlyphFlags = loadFlags; - fDoLinearMetrics = linearMetrics; } - // now create the FT_Size - - { - FT_Error err; + FT_Error err = FT_New_Size(fFace, &fFTSize); + if (err != 0) { + SkDEBUGF(("FT_New_Size returned %x for face %s\n", err, fFace->family_name)); + fFace = NULL; + return; + } + err = FT_Activate_Size(fFTSize); + if (err != 0) { + SkDEBUGF(("FT_Activate_Size(%08x, 0x%x, 0x%x) returned 0x%x\n", fFace, fScaleX, fScaleY, + err)); + fFTSize = NULL; + return; + } - err = FT_New_Size(fFace, &fFTSize); + if (FT_IS_SCALABLE(fFace)) { + err = FT_Set_Char_Size(fFace, SkFixedToFDot6(fScaleX), SkFixedToFDot6(fScaleY), 72, 72); if (err != 0) { - SkDEBUGF(("SkScalerContext_FreeType::FT_New_Size(%x): FT_Set_Char_Size(0x%x, 0x%x) returned 0x%x\n", - fFaceRec->fFontID, fScaleX, fScaleY, err)); + SkDEBUGF(("FT_Set_CharSize(%08x, 0x%x, 0x%x) returned 0x%x\n", + fFace, fScaleX, fScaleY, err)); fFace = NULL; return; } - - err = FT_Activate_Size(fFTSize); - if (err != 0) { - SkDEBUGF(("SkScalerContext_FreeType::FT_Activate_Size(%x, 0x%x, 0x%x) returned 0x%x\n", - fFaceRec->fFontID, fScaleX, fScaleY, err)); - fFTSize = NULL; - } - - err = FT_Set_Char_Size( fFace, - SkFixedToFDot6(fScaleX), SkFixedToFDot6(fScaleY), - 72, 72); - if (err != 0) { - SkDEBUGF(("SkScalerContext_FreeType::FT_Set_Char_Size(%x, 0x%x, 0x%x) returned 0x%x\n", - fFaceRec->fFontID, fScaleX, fScaleY, err)); - fFace = NULL; - return; + FT_Set_Transform(fFace, &fMatrix22, NULL); + } else if (FT_HAS_FIXED_SIZES(fFace)) { + fStrikeIndex = chooseBitmapStrike(fFace, fScaleY); + if (fStrikeIndex == -1) { + SkDEBUGF(("no glyphs for font \"%s\" size %f?\n", + fFace->family_name, SkFixedToScalar(fScaleY))); + } else { + // FreeType does no provide linear metrics for bitmap fonts. + linearMetrics = false; + + // FreeType documentation says: + // FT_LOAD_NO_BITMAP -- Ignore bitmap strikes when loading. + // Bitmap-only fonts ignore this flag. + // + // However, in FreeType 2.5.1 color bitmap only fonts do not ignore this flag. + // Force this flag off for bitmap only fonts. + fLoadGlyphFlags &= ~FT_LOAD_NO_BITMAP; } - - FT_Set_Transform( fFace, &fMatrix22, NULL); + } else { + SkDEBUGF(("unknown kind of font \"%s\" size %f?\n", + fFace->family_name, SkFixedToScalar(fScaleY))); } + + fDoLinearMetrics = linearMetrics; } SkScalerContext_FreeType::~SkScalerContext_FreeType() { @@ -932,7 +1008,6 @@ SkScalerContext_FreeType::~SkScalerContext_FreeType() { unref_ft_face(fFace); } if (--gFTCount == 0) { -// SkDEBUGF(("FT_Done_FreeType\n")); FT_Done_FreeType(gFTLibrary); SkDEBUGCODE(gFTLibrary = NULL;) } @@ -942,18 +1017,18 @@ SkScalerContext_FreeType::~SkScalerContext_FreeType() { this face with other context (at different sizes). */ FT_Error SkScalerContext_FreeType::setupSize() { - FT_Error err = FT_Activate_Size(fFTSize); - + FT_Error err = FT_Activate_Size(fFTSize); if (err != 0) { SkDEBUGF(("SkScalerContext_FreeType::FT_Activate_Size(%x, 0x%x, 0x%x) returned 0x%x\n", - fFaceRec->fFontID, fScaleX, fScaleY, err)); + fFaceRec->fFontID, fScaleX, fScaleY, err)); fFTSize = NULL; - } else { - // seems we need to reset this every time (not sure why, but without it - // I get random italics from some other fFTSize) - FT_Set_Transform( fFace, &fMatrix22, NULL); + return err; } - return err; + + // seems we need to reset this every time (not sure why, but without it + // I get random italics from some other fFTSize) + FT_Set_Transform(fFace, &fMatrix22, NULL); + return 0; } unsigned SkScalerContext_FreeType::generateGlyphCount() { @@ -1063,6 +1138,17 @@ void SkScalerContext_FreeType::updateGlyphIfLCD(SkGlyph* glyph) { } } +inline void scaleGlyphMetrics(SkGlyph& glyph, SkScalar scale) { + glyph.fWidth *= scale; + glyph.fHeight *= scale; + glyph.fTop *= scale; + glyph.fLeft *= scale; + + SkFixed fixedScale = SkScalarToFixed(scale); + glyph.fAdvanceX = SkFixedMul(glyph.fAdvanceX, fixedScale); + glyph.fAdvanceY = SkFixedMul(glyph.fAdvanceY, fixedScale); +} + void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) { SkAutoMutexAcquire ac(gFTMutex); @@ -1087,32 +1173,28 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) { } switch ( fFace->glyph->format ) { - case FT_GLYPH_FORMAT_OUTLINE: { - FT_BBox bbox; - + case FT_GLYPH_FORMAT_OUTLINE: if (0 == fFace->glyph->outline.n_contours) { glyph->fWidth = 0; glyph->fHeight = 0; glyph->fTop = 0; glyph->fLeft = 0; - break; - } - - if (fRec.fFlags & kEmbolden_Flag) { - emboldenOutline(fFace, &fFace->glyph->outline); - } - - getBBoxForCurrentGlyph(glyph, &bbox, true); + } else { + if (fRec.fFlags & kEmbolden_Flag && !(fFace->style_flags & FT_STYLE_FLAG_BOLD)) { + emboldenOutline(fFace, &fFace->glyph->outline); + } - glyph->fWidth = SkToU16(SkFDot6Floor(bbox.xMax - bbox.xMin)); - glyph->fHeight = SkToU16(SkFDot6Floor(bbox.yMax - bbox.yMin)); - glyph->fTop = -SkToS16(SkFDot6Floor(bbox.yMax)); - glyph->fLeft = SkToS16(SkFDot6Floor(bbox.xMin)); + FT_BBox bbox; + getBBoxForCurrentGlyph(glyph, &bbox, true); - updateGlyphIfLCD(glyph); + glyph->fWidth = SkToU16(SkFDot6Floor(bbox.xMax - bbox.xMin)); + glyph->fHeight = SkToU16(SkFDot6Floor(bbox.yMax - bbox.yMin)); + glyph->fTop = -SkToS16(SkFDot6Floor(bbox.yMax)); + glyph->fLeft = SkToS16(SkFDot6Floor(bbox.xMin)); + updateGlyphIfLCD(glyph); + } break; - } case FT_GLYPH_FORMAT_BITMAP: if (fRec.fFlags & kEmbolden_Flag) { @@ -1129,6 +1211,10 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) { fFace->glyph->bitmap_top += SkFDot6Floor(vector.y); } + if (fFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) { + glyph->fMaskFormat = SkMask::kARGB32_Format; + } + glyph->fWidth = SkToU16(fFace->glyph->bitmap.width); glyph->fHeight = SkToU16(fFace->glyph->bitmap.rows); glyph->fTop = -SkToS16(fFace->glyph->bitmap_top); @@ -1163,6 +1249,11 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) { } } + if (fFace->glyph->format == FT_GLYPH_FORMAT_BITMAP && fScaleY && fFace->size->metrics.y_ppem) { + // NOTE: both dimensions are scaled by y_ppem. this is WAI. + scaleGlyphMetrics(*glyph, SkScalarDiv(SkFixedToScalar(fScaleY), + SkIntToScalar(fFace->size->metrics.y_ppem))); + } #ifdef ENABLE_GLYPH_SPEW SkDEBUGF(("FT_Set_Char_Size(this:%p sx:%x sy:%x ", this, fScaleX, fScaleY)); @@ -1250,77 +1341,104 @@ void SkScalerContext_FreeType::generateFontMetrics(SkPaint::FontMetrics* mx, } FT_Face face = fFace; - int upem = face->units_per_EM; - if (upem <= 0) { - goto ERROR; - } - - SkPoint pts[6]; - SkFixed ys[6]; + SkScalar scaleX = fScale.x(); SkScalar scaleY = fScale.y(); - SkScalar mxy = fMatrix22Scalar.getSkewX(); - SkScalar myy = fMatrix22Scalar.getScaleY(); - SkScalar xmin = SkIntToScalar(face->bbox.xMin) / upem; - SkScalar xmax = SkIntToScalar(face->bbox.xMax) / upem; - - int leading = face->height - (face->ascender + -face->descender); - if (leading < 0) { - leading = 0; + SkScalar mxy = fMatrix22Scalar.getSkewX() * scaleY; + SkScalar myy = fMatrix22Scalar.getScaleY() * scaleY; + + // fetch units/EM from "head" table if needed (ie for bitmap fonts) + SkScalar upem = SkIntToScalar(face->units_per_EM); + if (!upem) { + TT_Header* ttHeader = (TT_Header*)FT_Get_Sfnt_Table(face, ft_sfnt_head); + if (ttHeader) { + upem = SkIntToScalar(ttHeader->Units_Per_EM); + } } - // Try to get the OS/2 table from the font. This contains the specific - // average font width metrics which Windows uses. + // use the os/2 table as a source of reasonable defaults. + SkScalar x_height = 0.0f; + SkScalar avgCharWidth = 0.0f; TT_OS2* os2 = (TT_OS2*) FT_Get_Sfnt_Table(face, ft_sfnt_os2); - - ys[0] = -face->bbox.yMax; - ys[1] = -face->ascender; - ys[2] = -face->descender; - ys[3] = -face->bbox.yMin; - ys[4] = leading; - ys[5] = os2 ? os2->xAvgCharWidth : 0; - - SkScalar x_height; - if (os2 && os2->sxHeight) { - x_height = fScale.x() * os2->sxHeight / upem; - } else { - const FT_UInt x_glyph = FT_Get_Char_Index(fFace, 'x'); - if (x_glyph) { - FT_BBox bbox; - FT_Load_Glyph(fFace, x_glyph, fLoadGlyphFlags); - if (fRec.fFlags & kEmbolden_Flag) { - emboldenOutline(fFace, &fFace->glyph->outline); + if (os2) { + x_height = scaleX * SkIntToScalar(os2->sxHeight) / upem; + avgCharWidth = SkIntToScalar(os2->xAvgCharWidth) / upem; + } + + // pull from format-specific metrics as needed + SkScalar ascent, descent, leading, xmin, xmax, ymin, ymax; + if (face->face_flags & FT_FACE_FLAG_SCALABLE) { // scalable outline font + ascent = -SkIntToScalar(face->ascender) / upem; + descent = -SkIntToScalar(face->descender) / upem; + leading = SkIntToScalar(face->height + (face->descender - face->ascender)) / upem; + xmin = SkIntToScalar(face->bbox.xMin) / upem; + xmax = SkIntToScalar(face->bbox.xMax) / upem; + ymin = -SkIntToScalar(face->bbox.yMin) / upem; + ymax = -SkIntToScalar(face->bbox.yMax) / upem; + // we may be able to synthesize x_height from outline + if (!x_height) { + const FT_UInt x_glyph = FT_Get_Char_Index(fFace, 'x'); + if (x_glyph) { + FT_BBox bbox; + FT_Load_Glyph(fFace, x_glyph, fLoadGlyphFlags); + if ((fRec.fFlags & kEmbolden_Flag) && !(fFace->style_flags & FT_STYLE_FLAG_BOLD)) { + emboldenOutline(fFace, &fFace->glyph->outline); + } + FT_Outline_Get_CBox(&fFace->glyph->outline, &bbox); + x_height = SkIntToScalar(bbox.yMax) / 64.0f; } - FT_Outline_Get_CBox(&fFace->glyph->outline, &bbox); - x_height = bbox.yMax / 64.0f; - } else { - x_height = 0; } + } else if (fStrikeIndex != -1) { // bitmap strike metrics + SkScalar xppem = SkIntToScalar(face->size->metrics.x_ppem); + SkScalar yppem = SkIntToScalar(face->size->metrics.y_ppem); + ascent = -SkIntToScalar(face->size->metrics.ascender) / (yppem * 64.0f); + descent = -SkIntToScalar(face->size->metrics.descender) / (yppem * 64.0f); + leading = (SkIntToScalar(face->size->metrics.height) / (yppem * 64.0f)) + + ascent - descent; + xmin = 0.0f; + xmax = SkIntToScalar(face->available_sizes[fStrikeIndex].width) / xppem; + ymin = descent + leading; + ymax = ascent - descent; + if (!x_height) { + x_height = -ascent; + } + if (!avgCharWidth) { + avgCharWidth = xmax - xmin; + } + } else { + goto ERROR; + } + + // synthesize elements that were not provided by the os/2 table or format-specific metrics + if (!x_height) { + x_height = -ascent; + } + if (!avgCharWidth) { + avgCharWidth = xmax - xmin; } - // convert upem-y values into scalar points - for (int i = 0; i < 6; i++) { - SkScalar y = scaleY * ys[i] / upem; - pts[i].set(y * mxy, y * myy); + // disallow negative linespacing + if (leading < 0.0f) { + leading = 0.0f; } if (mx) { - mx->fTop = pts[0].fX; - mx->fAscent = pts[1].fX; - mx->fDescent = pts[2].fX; - mx->fBottom = pts[3].fX; - mx->fLeading = pts[4].fX; - mx->fAvgCharWidth = pts[5].fX; + mx->fTop = ymax * mxy; + mx->fAscent = ascent * mxy; + mx->fDescent = descent * mxy; + mx->fBottom = ymin * mxy; + mx->fLeading = leading * mxy; + mx->fAvgCharWidth = avgCharWidth * mxy; mx->fXMin = xmin; mx->fXMax = xmax; mx->fXHeight = x_height; } if (my) { - my->fTop = pts[0].fY; - my->fAscent = pts[1].fY; - my->fDescent = pts[2].fY; - my->fBottom = pts[3].fY; - my->fLeading = pts[4].fY; - my->fAvgCharWidth = pts[5].fY; + my->fTop = ymax * myy; + my->fAscent = ascent * myy; + my->fDescent = descent * myy; + my->fBottom = ymin * myy; + my->fLeading = leading * myy; + my->fAvgCharWidth = avgCharWidth * myy; my->fXMin = xmin; my->fXMax = xmax; my->fXHeight = x_height; diff --git a/ports/SkFontHost_FreeType_common.cpp b/ports/SkFontHost_FreeType_common.cpp index 2c486847..065a83a4 100644 --- a/ports/SkFontHost_FreeType_common.cpp +++ b/ports/SkFontHost_FreeType_common.cpp @@ -6,17 +6,32 @@ * found in the LICENSE file. */ +#include "SkBitmap.h" +#include "SkCanvas.h" +#include "SkColor.h" #include "SkColorPriv.h" #include "SkFDot6.h" #include "SkFontHost_FreeType_common.h" #include "SkPath.h" #include <ft2build.h> -#include FT_OUTLINE_H +#include FT_FREETYPE_H #include FT_BITMAP_H +#include FT_IMAGE_H +#include FT_OUTLINE_H // In the past, FT_GlyphSlot_Own_Bitmap was defined in this header file. #include FT_SYNTHESIS_H +// FT_LOAD_COLOR and the corresponding FT_Pixel_Mode::FT_PIXEL_MODE_BGRA +// were introduced in FreeType 2.5.0. +// The following may be removed once FreeType 2.5.0 is required to build. +#ifndef FT_LOAD_COLOR +# define FT_LOAD_COLOR ( 1L << 20 ) +# define FT_PIXEL_MODE_BGRA 7 +#endif + +//#define SK_SHOW_TEXT_BLIT_COVERAGE + static FT_Pixel_Mode compute_pixel_mode(SkMask::Format format) { switch (format) { case SkMask::kBW_Format: @@ -29,13 +44,20 @@ static FT_Pixel_Mode compute_pixel_mode(SkMask::Format format) { /////////////////////////////////////////////////////////////////////////////// -static uint16_t packTriple(unsigned r, unsigned g, unsigned b) { - return SkPackRGB16(r >> 3, g >> 2, b >> 3); +static uint16_t packTriple(U8CPU r, U8CPU g, U8CPU b) { +#ifdef SK_SHOW_TEXT_BLIT_COVERAGE + r = SkTMax(r, (U8CPU)0x40); + g = SkTMax(g, (U8CPU)0x40); + b = SkTMax(b, (U8CPU)0x40); +#endif + return SkPack888ToRGB16(r, g, b); } static uint16_t grayToRGB16(U8CPU gray) { - SkASSERT(gray <= 255); - return SkPackRGB16(gray >> 3, gray >> 2, gray >> 3); +#ifdef SK_SHOW_TEXT_BLIT_COVERAGE + gray = SkTMax(gray, (U8CPU)0x40); +#endif + return SkPack888ToRGB16(gray, gray, gray); } static int bittst(const uint8_t data[], int bitOffset) { @@ -44,78 +66,271 @@ static int bittst(const uint8_t data[], int bitOffset) { return lowBit & 1; } +/** + * Copies a FT_Bitmap into an SkMask with the same dimensions. + * + * FT_PIXEL_MODE_MONO + * FT_PIXEL_MODE_GRAY + * FT_PIXEL_MODE_LCD + * FT_PIXEL_MODE_LCD_V + */ template<bool APPLY_PREBLEND> -static void copyFT2LCD16(const SkGlyph& glyph, const FT_Bitmap& bitmap, - int lcdIsBGR, bool lcdIsVert, const uint8_t* tableR, - const uint8_t* tableG, const uint8_t* tableB) { - if (lcdIsVert) { - SkASSERT(3 * glyph.fHeight == bitmap.rows); - } else { - SkASSERT(glyph.fHeight == bitmap.rows); +static void copyFT2LCD16(const FT_Bitmap& bitmap, const SkMask& mask, int lcdIsBGR, + const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) +{ + SkASSERT(SkMask::kLCD16_Format == mask.fFormat); + if (FT_PIXEL_MODE_LCD != bitmap.pixel_mode) { + SkASSERT(mask.fBounds.width() == bitmap.width); + } + if (FT_PIXEL_MODE_LCD_V != bitmap.pixel_mode) { + SkASSERT(mask.fBounds.height() == bitmap.rows); } - uint16_t* dst = reinterpret_cast<uint16_t*>(glyph.fImage); - const size_t dstRB = glyph.rowBytes(); - const int width = glyph.fWidth; const uint8_t* src = bitmap.buffer; + uint16_t* dst = reinterpret_cast<uint16_t*>(mask.fImage); + const size_t dstRB = mask.fRowBytes; + + const int width = mask.fBounds.width(); + const int height = mask.fBounds.height(); switch (bitmap.pixel_mode) { - case FT_PIXEL_MODE_MONO: { - for (int y = 0; y < glyph.fHeight; ++y) { + case FT_PIXEL_MODE_MONO: + for (int y = height; y --> 0;) { for (int x = 0; x < width; ++x) { dst[x] = -bittst(src, x); } dst = (uint16_t*)((char*)dst + dstRB); src += bitmap.pitch; } - } break; - case FT_PIXEL_MODE_GRAY: { - for (int y = 0; y < glyph.fHeight; ++y) { + break; + case FT_PIXEL_MODE_GRAY: + for (int y = height; y --> 0;) { for (int x = 0; x < width; ++x) { dst[x] = grayToRGB16(src[x]); } dst = (uint16_t*)((char*)dst + dstRB); src += bitmap.pitch; } - } break; - default: { - SkASSERT(lcdIsVert || (glyph.fWidth * 3 == bitmap.width)); - for (int y = 0; y < glyph.fHeight; y++) { - if (lcdIsVert) { // vertical stripes - const uint8_t* srcR = src; - const uint8_t* srcG = srcR + bitmap.pitch; - const uint8_t* srcB = srcG + bitmap.pitch; - if (lcdIsBGR) { - SkTSwap(srcR, srcB); - } + break; + case FT_PIXEL_MODE_LCD: + SkASSERT(3 * mask.fBounds.width() == bitmap.width); + for (int y = height; y --> 0;) { + const uint8_t* triple = src; + if (lcdIsBGR) { for (int x = 0; x < width; x++) { - dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(*srcR++, tableR), - sk_apply_lut_if<APPLY_PREBLEND>(*srcG++, tableG), - sk_apply_lut_if<APPLY_PREBLEND>(*srcB++, tableB)); + dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(triple[2], tableR), + sk_apply_lut_if<APPLY_PREBLEND>(triple[1], tableG), + sk_apply_lut_if<APPLY_PREBLEND>(triple[0], tableB)); + triple += 3; } - src += 3 * bitmap.pitch; - } else { // horizontal stripes - const uint8_t* triple = src; - if (lcdIsBGR) { - for (int x = 0; x < width; x++) { - dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(triple[2], tableR), - sk_apply_lut_if<APPLY_PREBLEND>(triple[1], tableG), - sk_apply_lut_if<APPLY_PREBLEND>(triple[0], tableB)); - triple += 3; - } - } else { - for (int x = 0; x < width; x++) { - dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(triple[0], tableR), - sk_apply_lut_if<APPLY_PREBLEND>(triple[1], tableG), - sk_apply_lut_if<APPLY_PREBLEND>(triple[2], tableB)); - triple += 3; - } + } else { + for (int x = 0; x < width; x++) { + dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(triple[0], tableR), + sk_apply_lut_if<APPLY_PREBLEND>(triple[1], tableG), + sk_apply_lut_if<APPLY_PREBLEND>(triple[2], tableB)); + triple += 3; } - src += bitmap.pitch; } + src += bitmap.pitch; dst = (uint16_t*)((char*)dst + dstRB); } - } break; + break; + case FT_PIXEL_MODE_LCD_V: + SkASSERT(3 * mask.fBounds.height() == bitmap.rows); + for (int y = height; y --> 0;) { + const uint8_t* srcR = src; + const uint8_t* srcG = srcR + bitmap.pitch; + const uint8_t* srcB = srcG + bitmap.pitch; + if (lcdIsBGR) { + SkTSwap(srcR, srcB); + } + for (int x = 0; x < width; x++) { + dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(*srcR++, tableR), + sk_apply_lut_if<APPLY_PREBLEND>(*srcG++, tableG), + sk_apply_lut_if<APPLY_PREBLEND>(*srcB++, tableB)); + } + src += 3 * bitmap.pitch; + dst = (uint16_t*)((char*)dst + dstRB); + } + break; + default: + SkDEBUGF(("FT_Pixel_Mode %d", bitmap.pixel_mode)); + SkDEBUGFAIL("unsupported FT_Pixel_Mode for LCD16"); + break; + } +} + +/** + * Copies a FT_Bitmap into an SkMask with the same dimensions. + * + * Yes, No, Never Requested, Never Produced + * + * kBW kA8 k3D kARGB32 kLCD16 kLCD32 + * FT_PIXEL_MODE_MONO Y Y NR N Y NR + * FT_PIXEL_MODE_GRAY N Y NR N Y NR + * FT_PIXEL_MODE_GRAY2 NP NP NR NP NP NR + * FT_PIXEL_MODE_GRAY4 NP NP NR NP NP NR + * FT_PIXEL_MODE_LCD NP NP NR NP NP NR + * FT_PIXEL_MODE_LCD_V NP NP NR NP NP NR + * FT_PIXEL_MODE_BGRA N N NR Y N NR + * + * TODO: All of these N need to be Y or otherwise ruled out. + */ +static void copyFTBitmap(const FT_Bitmap& srcFTBitmap, SkMask& dstMask) { + SkASSERT(dstMask.fBounds.width() == srcFTBitmap.width); + SkASSERT(dstMask.fBounds.height() == srcFTBitmap.rows); + + const uint8_t* src = reinterpret_cast<const uint8_t*>(srcFTBitmap.buffer); + const FT_Pixel_Mode srcFormat = static_cast<FT_Pixel_Mode>(srcFTBitmap.pixel_mode); + // FT_Bitmap::pitch is an int and allowed to be negative. + const int srcPitch = srcFTBitmap.pitch; + const size_t srcRowBytes = SkTAbs(srcPitch); + + uint8_t* dst = dstMask.fImage; + const SkMask::Format dstFormat = static_cast<SkMask::Format>(dstMask.fFormat); + const size_t dstRowBytes = dstMask.fRowBytes; + + const size_t width = srcFTBitmap.width; + const size_t height = srcFTBitmap.rows; + + if (SkMask::kLCD16_Format == dstFormat) { + copyFT2LCD16<false>(srcFTBitmap, dstMask, false, NULL, NULL, NULL); + return; + } + + if ((FT_PIXEL_MODE_MONO == srcFormat && SkMask::kBW_Format == dstFormat) || + (FT_PIXEL_MODE_GRAY == srcFormat && SkMask::kA8_Format == dstFormat)) + { + size_t commonRowBytes = SkTMin(srcRowBytes, dstRowBytes); + for (size_t y = height; y --> 0;) { + memcpy(dst, src, commonRowBytes); + src += srcPitch; + dst += dstRowBytes; + } + } else if (FT_PIXEL_MODE_MONO == srcFormat && SkMask::kA8_Format == dstFormat) { + for (size_t y = height; y --> 0;) { + uint8_t byte = 0; + int bits = 0; + const uint8_t* src_row = src; + uint8_t* dst_row = dst; + for (size_t x = width; x --> 0;) { + if (0 == bits) { + byte = *src_row++; + bits = 8; + } + *dst_row++ = byte & 0x80 ? 0xff : 0x00; + bits--; + byte <<= 1; + } + src += srcPitch; + dst += dstRowBytes; + } + } else if (FT_PIXEL_MODE_BGRA == srcFormat && SkMask::kARGB32_Format == dstFormat) { + // FT_PIXEL_MODE_BGRA is pre-multiplied. + for (size_t y = height; y --> 0;) { + const uint8_t* src_row = src; + SkPMColor* dst_row = reinterpret_cast<SkPMColor*>(dst); + for (size_t x = 0; x < width; ++x) { + uint8_t b = *src_row++; + uint8_t g = *src_row++; + uint8_t r = *src_row++; + uint8_t a = *src_row++; + *dst_row++ = SkPackARGB32(a, r, g, b); +#ifdef SK_SHOW_TEXT_BLIT_COVERAGE + *(dst_row-1) = SkFourByteInterp256(*(dst_row-1), SK_ColorWHITE, 0x40); +#endif + } + src += srcPitch; + dst += dstRowBytes; + } + } else { + SkDEBUGF(("FT_Pixel_Mode %d, SkMask::Format %d\n", srcFormat, dstFormat)); + SkDEBUGFAIL("unsupported combination of FT_Pixel_Mode and SkMask::Format"); + } +} + +static inline int convert_8_to_1(unsigned byte) { + SkASSERT(byte <= 0xFF); + // Arbitrary decision that making the cutoff at 1/4 instead of 1/2 in general looks better. + return (byte >> 6) != 0; +} + +static uint8_t pack_8_to_1(const uint8_t alpha[8]) { + unsigned bits = 0; + for (int i = 0; i < 8; ++i) { + bits <<= 1; + bits |= convert_8_to_1(alpha[i]); + } + return SkToU8(bits); +} + +static void packA8ToA1(const SkMask& mask, const uint8_t* src, size_t srcRB) { + const int height = mask.fBounds.height(); + const int width = mask.fBounds.width(); + const int octs = width >> 3; + const int leftOverBits = width & 7; + + uint8_t* dst = mask.fImage; + const int dstPad = mask.fRowBytes - SkAlign8(width)/8; + SkASSERT(dstPad >= 0); + + const int srcPad = srcRB - width; + SkASSERT(srcPad >= 0); + + for (int y = 0; y < height; ++y) { + for (int i = 0; i < octs; ++i) { + *dst++ = pack_8_to_1(src); + src += 8; + } + if (leftOverBits > 0) { + unsigned bits = 0; + int shift = 7; + for (int i = 0; i < leftOverBits; ++i, --shift) { + bits |= convert_8_to_1(*src++) << shift; + } + *dst++ = bits; + } + src += srcPad; + dst += dstPad; + } +} + +inline SkMask::Format SkMaskFormat_for_SkBitmapConfig(SkBitmap::Config config) { + switch (config) { + case SkBitmap::kA8_Config: + return SkMask::kA8_Format; + case SkBitmap::kARGB_8888_Config: + return SkMask::kARGB32_Format; + default: + SkDEBUGFAIL("unsupported SkBitmap::Config"); + return SkMask::kA8_Format; + } +} + +inline SkBitmap::Config SkBitmapConfig_for_FTPixelMode(FT_Pixel_Mode pixel_mode) { + switch (pixel_mode) { + case FT_PIXEL_MODE_MONO: + case FT_PIXEL_MODE_GRAY: + return SkBitmap::kA8_Config; + case FT_PIXEL_MODE_BGRA: + return SkBitmap::kARGB_8888_Config; + default: + SkDEBUGFAIL("unsupported FT_PIXEL_MODE"); + return SkBitmap::kA8_Config; + } +} + +inline SkBitmap::Config SkBitmapConfig_for_SkMaskFormat(SkMask::Format format) { + switch (format) { + case SkMask::kBW_Format: + case SkMask::kA8_Format: + case SkMask::kLCD16_Format: + return SkBitmap::kA8_Config; + case SkMask::kARGB32_Format: + return SkBitmap::kARGB_8888_Config; + default: + SkDEBUGFAIL("unsupported destination SkBitmap::Config"); + return SkBitmap::kA8_Config; } } @@ -129,7 +344,8 @@ void SkScalerContext_FreeType_Base::generateGlyphImage(FT_Face face, const SkGly FT_BBox bbox; FT_Bitmap target; - if (fRec.fFlags & SkScalerContext::kEmbolden_Flag) { + if (fRec.fFlags & SkScalerContext::kEmbolden_Flag && + !(face->style_flags & FT_STYLE_FLAG_BOLD)) { emboldenOutline(face, outline); } @@ -154,11 +370,13 @@ void SkScalerContext_FreeType_Base::generateGlyphImage(FT_Face face, const SkGly if (SkMask::kLCD16_Format == glyph.fMaskFormat) { FT_Render_Glyph(face->glyph, doVert ? FT_RENDER_MODE_LCD_V : FT_RENDER_MODE_LCD); + SkMask mask; + glyph.toMask(&mask); if (fPreBlend.isApplicable()) { - copyFT2LCD16<true>(glyph, face->glyph->bitmap, doBGR, doVert, + copyFT2LCD16<true>(face->glyph->bitmap, mask, doBGR, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); } else { - copyFT2LCD16<false>(glyph, face->glyph->bitmap, doBGR, doVert, + copyFT2LCD16<false>(face->glyph->bitmap, mask, doBGR, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); } } else { @@ -166,8 +384,7 @@ void SkScalerContext_FreeType_Base::generateGlyphImage(FT_Face face, const SkGly target.rows = glyph.fHeight; target.pitch = glyph.rowBytes(); target.buffer = reinterpret_cast<uint8_t*>(glyph.fImage); - target.pixel_mode = compute_pixel_mode( - (SkMask::Format)fRec.fMaskFormat); + target.pixel_mode = compute_pixel_mode( (SkMask::Format)fRec.fMaskFormat); target.num_grays = 256; memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight); @@ -176,71 +393,106 @@ void SkScalerContext_FreeType_Base::generateGlyphImage(FT_Face face, const SkGly } break; case FT_GLYPH_FORMAT_BITMAP: { - if (fRec.fFlags & SkScalerContext::kEmbolden_Flag) { + FT_Pixel_Mode pixel_mode = static_cast<FT_Pixel_Mode>(face->glyph->bitmap.pixel_mode); + SkMask::Format maskFormat = static_cast<SkMask::Format>(glyph.fMaskFormat); + + // Assume that the other formats do not exist. + SkASSERT(FT_PIXEL_MODE_MONO == pixel_mode || + FT_PIXEL_MODE_GRAY == pixel_mode || + FT_PIXEL_MODE_BGRA == pixel_mode); + + // These are the only formats this ScalerContext should request. + SkASSERT(SkMask::kBW_Format == maskFormat || + SkMask::kA8_Format == maskFormat || + SkMask::kARGB32_Format == maskFormat || + SkMask::kLCD16_Format == maskFormat); + + if (fRec.fFlags & SkScalerContext::kEmbolden_Flag && + !(face->style_flags & FT_STYLE_FLAG_BOLD)) + { FT_GlyphSlot_Own_Bitmap(face->glyph); - FT_Bitmap_Embolden(face->glyph->library, &face->glyph->bitmap, kBitmapEmboldenStrength, 0); + FT_Bitmap_Embolden(face->glyph->library, &face->glyph->bitmap, + kBitmapEmboldenStrength, 0); } - SkASSERT_CONTINUE(glyph.fWidth == face->glyph->bitmap.width); - SkASSERT_CONTINUE(glyph.fHeight == face->glyph->bitmap.rows); - SkASSERT_CONTINUE(glyph.fTop == -face->glyph->bitmap_top); - SkASSERT_CONTINUE(glyph.fLeft == face->glyph->bitmap_left); - - const uint8_t* src = (const uint8_t*)face->glyph->bitmap.buffer; - uint8_t* dst = (uint8_t*)glyph.fImage; - - if (face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY || - (face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO && - glyph.fMaskFormat == SkMask::kBW_Format)) { - unsigned srcRowBytes = face->glyph->bitmap.pitch; - unsigned dstRowBytes = glyph.rowBytes(); - unsigned minRowBytes = SkMin32(srcRowBytes, dstRowBytes); - unsigned extraRowBytes = dstRowBytes - minRowBytes; - - for (int y = face->glyph->bitmap.rows - 1; y >= 0; --y) { - memcpy(dst, src, minRowBytes); - memset(dst + minRowBytes, 0, extraRowBytes); - src += srcRowBytes; - dst += dstRowBytes; - } - } else if (face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO && - glyph.fMaskFormat == SkMask::kA8_Format) { - for (int y = 0; y < face->glyph->bitmap.rows; ++y) { - uint8_t byte = 0; - int bits = 0; - const uint8_t* src_row = src; - uint8_t* dst_row = dst; - - for (int x = 0; x < face->glyph->bitmap.width; ++x) { - if (!bits) { - byte = *src_row++; - bits = 8; - } - - *dst_row++ = byte & 0x80 ? 0xff : 0; - bits--; - byte <<= 1; - } - src += face->glyph->bitmap.pitch; - dst += glyph.rowBytes(); - } - } else if (SkMask::kLCD16_Format == glyph.fMaskFormat) { - if (fPreBlend.isApplicable()) { - copyFT2LCD16<true>(glyph, face->glyph->bitmap, doBGR, doVert, - fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); - } else { - copyFT2LCD16<false>(glyph, face->glyph->bitmap, doBGR, doVert, - fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); - } + // If no scaling needed, directly copy glyph bitmap. + if (glyph.fWidth == face->glyph->bitmap.width && + glyph.fHeight == face->glyph->bitmap.rows && + glyph.fTop == -face->glyph->bitmap_top && + glyph.fLeft == face->glyph->bitmap_left) + { + SkMask dstMask; + glyph.toMask(&dstMask); + copyFTBitmap(face->glyph->bitmap, dstMask); + break; + } + + // Otherwise, scale the bitmap. + + // Copy the FT_Bitmap into an SkBitmap (either A8 or ARGB) + SkBitmap unscaledBitmap; + unscaledBitmap.setConfig(SkBitmapConfig_for_FTPixelMode(pixel_mode), + face->glyph->bitmap.width, face->glyph->bitmap.rows); + unscaledBitmap.allocPixels(); + + SkMask unscaledBitmapAlias; + unscaledBitmapAlias.fImage = reinterpret_cast<uint8_t*>(unscaledBitmap.getPixels()); + unscaledBitmapAlias.fBounds.set(0, 0, unscaledBitmap.width(), unscaledBitmap.height()); + unscaledBitmapAlias.fRowBytes = unscaledBitmap.rowBytes(); + unscaledBitmapAlias.fFormat = SkMaskFormat_for_SkBitmapConfig(unscaledBitmap.config()); + copyFTBitmap(face->glyph->bitmap, unscaledBitmapAlias); + + // Wrap the glyph's mask in a bitmap, unless the glyph's mask is BW or LCD. + // BW requires an A8 target for resizing, which can then be down sampled. + // LCD should use a 4x A8 target, which will then be down sampled. + // For simplicity, LCD uses A8 and is replicated. + int bitmapRowBytes = 0; + if (SkMask::kBW_Format != maskFormat && SkMask::kLCD16_Format != maskFormat) { + bitmapRowBytes = glyph.rowBytes(); + } + SkBitmap dstBitmap; + dstBitmap.setConfig(SkBitmapConfig_for_SkMaskFormat(maskFormat), + glyph.fWidth, glyph.fHeight, bitmapRowBytes); + if (SkMask::kBW_Format == maskFormat || SkMask::kLCD16_Format == maskFormat) { + dstBitmap.allocPixels(); } else { - SkDEBUGFAIL("unknown glyph bitmap transform needed"); + dstBitmap.setPixels(glyph.fImage); } + + // Scale unscaledBitmap into dstBitmap. + SkCanvas canvas(dstBitmap); + canvas.clear(SK_ColorTRANSPARENT); + canvas.scale(SkIntToScalar(glyph.fWidth) / SkIntToScalar(face->glyph->bitmap.width), + SkIntToScalar(glyph.fHeight) / SkIntToScalar(face->glyph->bitmap.rows)); + SkPaint paint; + paint.setFilterLevel(SkPaint::kLow_FilterLevel); + canvas.drawBitmap(unscaledBitmap, 0, 0, &paint); + + // If the destination is BW or LCD, convert from A8. + if (SkMask::kBW_Format == maskFormat) { + // Copy the A8 dstBitmap into the A1 glyph.fImage. + SkMask dstMask; + glyph.toMask(&dstMask); + packA8ToA1(dstMask, dstBitmap.getAddr8(0, 0), dstBitmap.rowBytes()); + } else if (SkMask::kLCD16_Format == maskFormat) { + // Copy the A8 dstBitmap into the LCD16 glyph.fImage. + uint8_t* src = dstBitmap.getAddr8(0, 0); + uint16_t* dst = reinterpret_cast<uint16_t*>(glyph.fImage); + for (int y = dstBitmap.height(); y --> 0;) { + for (int x = 0; x < dstBitmap.width(); ++x) { + dst[x] = grayToRGB16(src[x]); + } + dst = (uint16_t*)((char*)dst + glyph.rowBytes()); + src += dstBitmap.rowBytes(); + } + } + } break; - default: - SkDEBUGFAIL("unknown glyph format"); - memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight); - return; + default: + SkDEBUGFAIL("unknown glyph format"); + memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight); + return; } // We used to always do this pre-USE_COLOR_LUMINANCE, but with colorlum, diff --git a/ports/SkFontHost_mac.cpp b/ports/SkFontHost_mac.cpp index 3c1a2748..fe3fd069 100755 --- a/ports/SkFontHost_mac.cpp +++ b/ports/SkFontHost_mac.cpp @@ -2107,7 +2107,13 @@ static SkTypeface* createFromDesc(CFStringRef cfFamilyName, return face; } - AutoCFRelease<CTFontRef> ctNamed(CTFontCreateWithName(cfFamilyName, 1, NULL)); + AutoCFRelease<CFDictionaryRef> fontFamilyNameDictionary( + CFDictionaryCreate(kCFAllocatorDefault, + (const void**)&kCTFontFamilyNameAttribute, (const void**)&cfFamilyName, + 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); + AutoCFRelease<CTFontDescriptorRef> fontDescriptor( + CTFontDescriptorCreateWithAttributes(fontFamilyNameDictionary)); + AutoCFRelease<CTFontRef> ctNamed(CTFontCreateWithFontDescriptor(fontDescriptor, 0, NULL)); CTFontRef ctFont = CTFontCreateCopyWithAttributes(ctNamed, 1, NULL, desc); if (NULL == ctFont) { return NULL; diff --git a/utils/android/ashmem.h b/utils/android/ashmem.h index 278192b4..94ffe1a3 100644 --- a/utils/android/ashmem.h +++ b/utils/android/ashmem.h @@ -16,6 +16,12 @@ extern "C" { int ashmem_create_region(const char *name, size_t size); int ashmem_set_prot_region(int fd, int prot); + +/** + * @return ASHMEM_NOT_PURGED if the memory was not purged. + * ASHMEM_WAS_PURGED if the memory was purged. + * -1 on error. + */ int ashmem_pin_region(int fd, size_t offset, size_t len); int ashmem_unpin_region(int fd, size_t offset, size_t len); int ashmem_get_size_region(int fd); |