diff --git a/gm/fontmgr.cpp b/gm/fontmgr.cpp index 79868982f4..a1e7ff2a68 100644 --- a/gm/fontmgr.cpp +++ b/gm/fontmgr.cpp @@ -15,6 +15,13 @@ #include "SkTypeface_win.h" #endif +static void scale(SkRect* rect, SkScalar scale) { + rect->fLeft *= scale; + rect->fTop *= scale; + rect->fRight *= scale; + rect->fBottom *= scale; +} + // limit this just so we don't take too long to draw #define MAX_FAMILIES 30 @@ -220,9 +227,96 @@ private: typedef GM INHERITED; }; +class FontMgrBoundsGM : public skiagm::GM { +public: + FontMgrBoundsGM() { + fName.set("fontmgr_bounds"); + fFM.reset(SkFontMgr::RefDefault()); + } + + static void show_bounds(SkCanvas* canvas, const SkPaint& paint, SkScalar x, SkScalar y, + SkColor boundsColor) { + const char str[] = "jyHO[]{}@-_&%$"; + + const SkTypeface* tf = paint.getTypeface(); + for (int i = 0; str[i]; ++i) { + canvas->drawText(&str[i], 1, x, y, paint); + } + + SkRect r = tf->getBounds(); + scale(&r, paint.getTextSize()); + r.offset(x, y); + SkPaint p(paint); + p.setColor(boundsColor); + canvas->drawRect(r, p); + } + +protected: + virtual SkString onShortName() { + return fName; + } + + virtual SkISize onISize() { + return SkISize::Make(1024, 850); + } + + virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE { + SkPaint paint; + paint.setAntiAlias(true); + paint.setSubpixelText(true); + paint.setTextSize(100); + paint.setStyle(SkPaint::kStroke_Style); + + const SkColor boundsColors[2] = { SK_ColorRED, SK_ColorBLUE }; + + SkFontMgr* fm = fFM; + int count = SkMin32(fm->countFamilies(), 32); + + int index = 0; + SkScalar x = 0, y = 0; + + canvas->translate(80, 120); + + for (int i = 0; i < count; ++i) { + SkAutoTUnref set(fm->createStyleSet(i)); + for (int j = 0; j < set->count(); ++j) { + SkSafeUnref(paint.setTypeface(set->createTypeface(j))); + if (paint.getTypeface()) { + show_bounds(canvas, paint, x, y, boundsColors[index & 1]); + index += 1; + x += 160; + if (0 == (index % 6)) { + x = 0; + y += 160; + } + if (index >= 30) { + return; + } + } + } + } + } + + virtual uint32_t onGetFlags() const SK_OVERRIDE { + // fontdescriptors (and therefore serialization) don't yet understand + // these new styles, so skip tests that exercise that for now. + + // If certain fonts are picked up (e.g. Microsoft Jhenghei 20MB for Regular, 12MB for Bold), + // the resulting pdf can be ~700MB and crashes Chrome's PDF viewer. + + return kSkipPicture_Flag | kSkipPipe_Flag | kSkipPDF_Flag; + } + +private: + SkAutoTUnref fFM; + SkString fName; + typedef GM INHERITED; +}; + ////////////////////////////////////////////////////////////////////////////// DEF_GM( return SkNEW(FontMgrGM); ) +DEF_GM( return SkNEW(FontMgrBoundsGM); ) DEF_GM( return SkNEW(FontMgrMatchGM); ) #ifdef SK_BUILD_FOR_WIN diff --git a/src/core/SkLazyPtr.h b/include/core/SkLazyPtr.h similarity index 98% rename from src/core/SkLazyPtr.h rename to include/core/SkLazyPtr.h index 7273079252..896dfbf88d 100644 --- a/src/core/SkLazyPtr.h +++ b/include/core/SkLazyPtr.h @@ -147,19 +147,19 @@ public: SkLazyPtr() : fPtr(NULL) {} ~SkLazyPtr() { if (fPtr) { Destroy((T*)fPtr); } } - T* get() { + T* get() const { T* ptr = (T*)sk_consume_load(&fPtr); return ptr ? ptr : Private::try_cas(&fPtr, SkNEW(T)); } template - T* get(const Create& create) { + T* get(const Create& create) const { T* ptr = (T*)sk_consume_load(&fPtr); return ptr ? ptr : Private::try_cas(&fPtr, create()); } private: - void* fPtr; + mutable void* fPtr; }; diff --git a/src/core/SkThreadPriv.h b/include/core/SkThreadPriv.h similarity index 100% rename from src/core/SkThreadPriv.h rename to include/core/SkThreadPriv.h diff --git a/include/core/SkTypeface.h b/include/core/SkTypeface.h index f67623674a..c3ff3641b7 100644 --- a/include/core/SkTypeface.h +++ b/include/core/SkTypeface.h @@ -12,6 +12,7 @@ #include "SkAdvancedTypefaceMetrics.h" #include "SkFontStyle.h" +#include "SkLazyPtr.h" #include "SkWeakRefCnt.h" class SkDescriptor; @@ -282,6 +283,13 @@ public: SkScalerContext* createScalerContext(const SkDescriptor*, bool allowFailure = false) const; + /** + * Return a rectangle (scaled to 1-pt) that represents the union of the bounds of all + * of the glyphs, but each one positioned at (0,). This may be conservatively large, and + * will not take into account any hinting or other size-specific adjustments. + */ + SkRect getBounds() const; + // PRIVATE / EXPERIMENTAL -- do not call void filterRec(SkScalerContextRec* rec) const { this->onFilterRec(rec); @@ -333,6 +341,8 @@ protected: virtual size_t onGetTableData(SkFontTableTag, size_t offset, size_t length, void* data) const = 0; + virtual bool onComputeBounds(SkRect*) const; + private: friend class SkGTypeface; friend class SkPDFFont; @@ -359,9 +369,13 @@ private: static SkTypeface* CreateDefault(int style); // SkLazyPtr requires an int, not a Style. static void DeleteDefault(SkTypeface*); - SkFontID fUniqueID; - SkFontStyle fStyle; - bool fIsFixedPitch; + struct BoundsComputer; +// friend struct BoundsComputer; + + SkLazyPtr fLazyBounds; + SkFontID fUniqueID; + SkFontStyle fStyle; + bool fIsFixedPitch; friend class SkPaint; friend class SkGlyphCache; // GetDefaultTypeface diff --git a/src/core/SkTypeface.cpp b/src/core/SkTypeface.cpp index 84cbdbfe66..c537d4aa2d 100644 --- a/src/core/SkTypeface.cpp +++ b/src/core/SkTypeface.cpp @@ -278,9 +278,61 @@ SkAdvancedTypefaceMetrics* SkTypeface::getAdvancedTypefaceMetrics( return result; } -/////////////////////////////////////////////////////////////////////////////// - bool SkTypeface::onGetKerningPairAdjustments(const uint16_t glyphs[], int count, int32_t adjustments[]) const { return false; } + +/////////////////////////////////////////////////////////////////////////////// + +#include "SkDescriptor.h" +#include "SkPaint.h" + +struct SkTypeface::BoundsComputer { + const SkTypeface& fTypeface; + + BoundsComputer(const SkTypeface& tf) : fTypeface(tf) {} + + SkRect* operator()() const { + SkRect* rect = SkNEW(SkRect); + if (!fTypeface.onComputeBounds(rect)) { + rect->setEmpty(); + } + return rect; + } +}; + +SkRect SkTypeface::getBounds() const { + return *fLazyBounds.get(BoundsComputer(*this)); +} + +bool SkTypeface::onComputeBounds(SkRect* bounds) const { + // we use a big size to ensure lots of significant bits from the scalercontext. + // then we scale back down to return our final answer (at 1-pt) + const SkScalar textSize = 2048; + const SkScalar invTextSize = 1 / textSize; + + SkPaint paint; + paint.setTypeface(const_cast(this)); + paint.setTextSize(textSize); + paint.setLinearText(true); + + SkScalerContext::Rec rec; + SkScalerContext::MakeRec(paint, NULL, NULL, &rec); + + SkAutoDescriptor ad(sizeof(rec) + SkDescriptor::ComputeOverhead(1)); + SkDescriptor* desc = ad.getDesc(); + desc->init(); + desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec); + + SkAutoTDelete ctx(this->createScalerContext(desc, true)); + if (ctx.get()) { + SkPaint::FontMetrics fm; + ctx->getFontMetrics(&fm); + bounds->set(fm.fXMin * invTextSize, fm.fTop * invTextSize, + fm.fXMax * invTextSize, fm.fBottom * invTextSize); + return true; + } + return false; +} +