summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Chromium Automerger <chromium-automerger@android>2013-12-11 13:32:27 +0000
committerAndroid Chromium Automerger <chromium-automerger@android>2013-12-11 13:32:27 +0000
commit24df3e66d89fff6e91ba76a9fe1a4f1e7c413402 (patch)
tree3c0229f3d6163ee2f43fb32a886cb15deb58227d
parent190df0154be02c5183f402e619b783580c73f258 (diff)
parent6168ea7d412448c88169a565f40bf86379d4bf9a (diff)
downloadsrc-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.cpp2
-rw-r--r--core/SkScaledImageCache.cpp4
-rw-r--r--gpu/SkGpuDevice.cpp4
-rw-r--r--gpu/effects/GrBicubicEffect.cpp17
-rw-r--r--gpu/effects/GrBicubicEffect.h44
-rw-r--r--image/SkImagePriv.cpp4
-rw-r--r--images/SkDecodingImageGenerator.cpp7
-rw-r--r--lazy/SkDiscardablePixelRef.cpp15
-rw-r--r--lazy/SkDiscardablePixelRef.h37
-rw-r--r--ports/SkDiscardableMemory_ashmem.cpp114
-rw-r--r--ports/SkFontConfigInterface_direct.cpp2
-rw-r--r--ports/SkFontHost_FreeType.cpp334
-rw-r--r--ports/SkFontHost_FreeType_common.cpp484
-rwxr-xr-xports/SkFontHost_mac.cpp8
-rw-r--r--utils/android/ashmem.h6
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 &params,
- 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);