diff options
-rw-r--r-- | Android.mk | 1 | ||||
-rw-r--r-- | include/core/SkCanvas.h | 19 | ||||
-rw-r--r-- | include/core/SkClipStack.h | 65 | ||||
-rw-r--r-- | include/core/SkDeque.h | 8 | ||||
-rw-r--r-- | include/core/SkDevice.h | 18 | ||||
-rw-r--r-- | include/core/SkScalerContext.h | 4 | ||||
-rw-r--r-- | include/core/SkTypeface.h | 23 | ||||
-rw-r--r-- | include/effects/SkBlurDrawLooper.h | 8 | ||||
-rw-r--r-- | include/effects/SkBlurMaskFilter.h | 6 | ||||
-rw-r--r-- | src/core/SkBitmap.cpp | 17 | ||||
-rw-r--r-- | src/core/SkCanvas.cpp | 97 | ||||
-rw-r--r-- | src/core/SkClipStack.cpp | 144 | ||||
-rw-r--r-- | src/core/SkDeque.cpp | 42 | ||||
-rw-r--r-- | src/core/SkDevice.cpp | 9 | ||||
-rw-r--r-- | src/core/SkScalerContext.cpp | 2 | ||||
-rw-r--r-- | src/effects/SkBlurDrawLooper.cpp | 25 | ||||
-rw-r--r-- | src/effects/SkBlurMask.cpp | 48 | ||||
-rw-r--r-- | src/effects/SkBlurMask.h | 7 | ||||
-rw-r--r-- | src/effects/SkBlurMaskFilter.cpp | 4 | ||||
-rw-r--r-- | src/effects/SkEmbossMaskFilter.cpp | 2 | ||||
-rw-r--r-- | src/effects/SkLayerRasterizer.cpp | 8 | ||||
-rw-r--r-- | src/ports/SkFontHost_FreeType.cpp | 25 | ||||
-rw-r--r-- | src/ports/SkFontHost_android.cpp | 39 | ||||
-rw-r--r-- | src/ports/SkFontHost_linux.cpp | 36 | ||||
-rw-r--r-- | src/ports/SkFontHost_win.cpp | 52 |
25 files changed, 567 insertions, 142 deletions
@@ -118,6 +118,7 @@ LOCAL_SRC_FILES:= \ src/core/SkBlitter_RGB16.cpp \ src/core/SkBlitter_Sprite.cpp \ src/core/SkCanvas.cpp \ + src/core/SkClipStack.cpp \ src/core/SkColor.cpp \ src/core/SkColorFilter.cpp \ src/core/SkColorTable.cpp \ diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h index b2a9fa9..103507c 100644 --- a/include/core/SkCanvas.h +++ b/include/core/SkCanvas.h @@ -20,6 +20,7 @@ #include "SkTypes.h" #include "SkBitmap.h" #include "SkDeque.h" +#include "SkClipStack.h" #include "SkPaint.h" #include "SkRefCnt.h" #include "SkPath.h" @@ -789,6 +790,7 @@ protected: private: class MCRec; + SkClipStack fClipStack; SkDeque fMCStack; // points to top of stack MCRec* fMCRec; @@ -848,6 +850,23 @@ private: SkMatrix fExternalMatrix, fExternalInverse; bool fUseExternalMatrix; + + class AutoValidateClip : ::SkNoncopyable { + public: + explicit AutoValidateClip(SkCanvas* canvas) : fCanvas(canvas) { + fCanvas->validateClip(); + } + ~AutoValidateClip() { fCanvas->validateClip(); } + + private: + const SkCanvas* fCanvas; + }; + +#ifdef SK_DEBUG + void validateClip() const; +#else + void validateClip() const {} +#endif }; /** Stack helper class to automatically call restoreToCount() on the canvas diff --git a/include/core/SkClipStack.h b/include/core/SkClipStack.h new file mode 100644 index 0000000..fb94155 --- /dev/null +++ b/include/core/SkClipStack.h @@ -0,0 +1,65 @@ +#ifndef SkClipStack_DEFINED +#define SkClipStack_DEFINED + +#include "SkDeque.h" +#include "SkRegion.h" + +struct SkRect; +class SkPath; + +class SkClipStack { +public: + SkClipStack(); + ~SkClipStack() {} + + void reset(); + + int getSaveCount() const { return fSaveCount; } + void save(); + void restore(); + + void clipDevRect(const SkIRect& ir, + SkRegion::Op op = SkRegion::kIntersect_Op) { + SkRect r; + r.set(ir); + this->clipDevRect(r, op); + } + void clipDevRect(const SkRect&, SkRegion::Op = SkRegion::kIntersect_Op); + void clipDevPath(const SkPath&, SkRegion::Op = SkRegion::kIntersect_Op); + + class B2FIter { + public: + B2FIter(const SkClipStack& stack); + + struct Clip { + const SkRect* fRect; // if non-null, this is a rect clip + const SkPath* fPath; // if non-null, this is a path clip + SkRegion::Op fOp; + }; + + /** + * Return the clip for this element in the iterator. If next() returns + * NULL, then the iterator is done. The type of clip is determined by + * the pointers fRect and fPath: + * + * fRect==NULL fPath!=NULL path clip + * fRect!=NULL fPath==NULL rect clip + * fRect==NULL fPath==NULL empty clip + */ + const Clip* next(); + + private: + Clip fClip; + SkDeque::F2BIter fIter; + }; + +private: + friend class B2FIter; + struct Rec; + + SkDeque fDeque; + int fSaveCount; +}; + +#endif + diff --git a/include/core/SkDeque.h b/include/core/SkDeque.h index cbed930..99c8dd4 100644 --- a/include/core/SkDeque.h +++ b/include/core/SkDeque.h @@ -42,7 +42,7 @@ public: void* push_front(); void* push_back(); - + void pop_front(); void pop_back(); @@ -50,9 +50,9 @@ private: struct Head; public: - class Iter { + class F2BIter { public: - Iter(const SkDeque& d); + F2BIter(const SkDeque& d); void* next(); private: @@ -67,7 +67,7 @@ private: size_t fElemSize; void* fInitialStorage; int fCount; - + friend class Iter; }; diff --git a/include/core/SkDevice.h b/include/core/SkDevice.h index 1b4630d..7791f34 100644 --- a/include/core/SkDevice.h +++ b/include/core/SkDevice.h @@ -23,6 +23,7 @@ #include "SkColor.h" #include "SkRefDict.h" +class SkClipStack; class SkDevice; class SkDraw; struct SkIRect; @@ -122,11 +123,20 @@ public: */ virtual SkGpuTexture* accessTexture() { return NULL; } - /** Called with the correct matrix and clip before this device is drawn - to using those settings. If your subclass overrides this, be sure to - call through to the base class as well. + /** + * Called with the correct matrix and clip before this device is drawn + * to using those settings. If your subclass overrides this, be sure to + * call through to the base class as well. + * + * The clipstack is another view of the clip. It records the actual + * geometry that went into building the region. It is present for devices + * that want to parse it, but is not required: the region is a complete + * picture of the current clip. (i.e. if you regionize all of the geometry + * in the clipstack, you will arrive at an equivalent region to the one + * passed in). */ - virtual void setMatrixClip(const SkMatrix&, const SkRegion&); + virtual void setMatrixClip(const SkMatrix&, const SkRegion&, + const SkClipStack&); /** Called when this device gains focus (i.e becomes the current device for drawing). diff --git a/include/core/SkScalerContext.h b/include/core/SkScalerContext.h index 0c93e2e..5937819 100644 --- a/include/core/SkScalerContext.h +++ b/include/core/SkScalerContext.h @@ -51,10 +51,6 @@ struct SkGlyph { fImage = NULL; fPath = NULL; fMaskFormat = MASK_FORMAT_UNKNOWN; -#ifdef SK_GPU_AWARE_GLYPHCACHE - fGLCacheOffset = SKGLYPH_GLCACHEOFFSET_INVALID; - fGLStrikePtr = NULL; -#endif } unsigned rowBytes() const { diff --git a/include/core/SkTypeface.h b/include/core/SkTypeface.h index cea6ab1..728ba31 100644 --- a/include/core/SkTypeface.h +++ b/include/core/SkTypeface.h @@ -56,7 +56,11 @@ public: /** Returns true if getStyle() has the kItalic bit set. */ bool isItalic() const { return (fStyle & kItalic) != 0; } - + + /** Returns true if the typeface is fixed-width + */ + bool isFixedWidth() const { return fIsFixedWidth; } + /** Return a 32bit value for this typeface, unique for the underlying font data. Will never return 0. */ @@ -72,11 +76,11 @@ public: handling either being null (treating null as the default font) */ static bool Equal(const SkTypeface* facea, const SkTypeface* faceb); - + /** Return a new reference to the typeface that most closely matches the requested familyName and style. Pass null as the familyName to return the default font for the requested style. Will never return null - + @param familyName May be NULL. The name of the font family. @param style The style (normal, bold, italic) of the typeface. @return reference to the closest-matching typeface. Call must call @@ -100,7 +104,7 @@ public: requested typeface and specified Style. Use this call if you want to pick a new style from the same family of the existing typeface. If family is NULL, this selects from the default font's family. - + @param family May be NULL. The name of the existing type face. @param s The style (normal, bold, italic) of the type face. @return reference to the closest-matching typeface. Call must call @@ -112,7 +116,7 @@ public: not a valid font file, returns null. */ static SkTypeface* CreateFromFile(const char path[]); - + /** Return a new typeface given a stream. If the stream is not a valid font file, returns null. Ownership of the stream is transferred, so the caller must not reference it again. @@ -123,7 +127,7 @@ public: typeface referencing the same font when Deserialize is called. */ void serialize(SkWStream*) const; - + /** Given the data previously written by serialize(), return a new instance to a typeface referring to the same font. If that font is not available, return null. If an instance is returned, the caller is responsible for @@ -142,13 +146,14 @@ public: protected: /** uniqueID must be unique (please!) and non-zero */ - SkTypeface(Style style, uint32_t uniqueID) - : fUniqueID(uniqueID), fStyle(style) {} + SkTypeface(Style style, uint32_t uniqueID, bool isFixedWidth = false) + : fUniqueID(uniqueID), fStyle(style), fIsFixedWidth(isFixedWidth) {} private: uint32_t fUniqueID; Style fStyle; - + bool fIsFixedWidth; + typedef SkRefCnt INHERITED; }; diff --git a/include/effects/SkBlurDrawLooper.h b/include/effects/SkBlurDrawLooper.h index 6f96ff6..e7af5c2 100644 --- a/include/effects/SkBlurDrawLooper.h +++ b/include/effects/SkBlurDrawLooper.h @@ -21,6 +21,7 @@ #include "SkColor.h" class SkMaskFilter; +class SkColorFilter; /** \class SkBlurDrawLooper This class draws a shadow of the object (possibly offset), and then draws @@ -35,9 +36,11 @@ public: The blur layer's dx/dy/radius aren't affected by the canvas transform. */ - kIgnoreTransform_BlurFlag = 0x01, + kIgnoreTransform_BlurFlag = 0x01, + kOverrideColor_BlurFlag = 0x02, + kHighQuality_BlurFlag = 0x04, /** mask for all blur flags */ - kAll_BlurFlag = 0x01 + kAll_BlurFlag = 0x07 }; SkBlurDrawLooper(SkScalar radius, SkScalar dx, SkScalar dy, SkColor color, @@ -64,6 +67,7 @@ private: SkCanvas* fCanvas; SkPaint* fPaint; SkMaskFilter* fBlur; + SkColorFilter* fColorFilter; SkScalar fDx, fDy; SkColor fBlurColor; SkColor fSavedColor; // remember the original diff --git a/include/effects/SkBlurMaskFilter.h b/include/effects/SkBlurMaskFilter.h index 0a68f54..0a0e8d2 100644 --- a/include/effects/SkBlurMaskFilter.h +++ b/include/effects/SkBlurMaskFilter.h @@ -35,9 +35,11 @@ public: enum BlurFlags { kNone_BlurFlag = 0x00, /** The blur layer's radius is not affected by transforms */ - kIgnoreTransform_BlurFlag = 0x01, + kIgnoreTransform_BlurFlag = 0x01, + /** Use a smother, higher qulity blur algorithm */ + kHighQuality_BlurFlag = 0x02, /** mask for all blur flags */ - kAll_BlurFlag = 0x01 + kAll_BlurFlag = 0x03 }; /** Create a blur maskfilter. diff --git a/src/core/SkBitmap.cpp b/src/core/SkBitmap.cpp index 297e156..9e6f993 100644 --- a/src/core/SkBitmap.cpp +++ b/src/core/SkBitmap.cpp @@ -1334,7 +1334,14 @@ void SkBitmap::flatten(SkFlattenableWriteBuffer& buffer) const { } else { buffer.write8(SERIALIZE_PIXELTYPE_RAW_NO_CTABLE); } - buffer.writePad(fPixels, this->getSize()); + buffer.writePad(fPixels, this->getSafeSize()); + // There is no writeZeroPad() fcn, so write individual bytes. + if (this->getSize() > this->getSafeSize()) { + size_t deltaSize = this->getSize() - this->getSafeSize(); + // Need aligned pointer to write into due to internal implementa- + // tion of SkWriter32. + memset(buffer.reserve(SkAlign4(deltaSize)), 0, deltaSize); + } } else { buffer.write8(SERIALIZE_PIXELTYPE_NONE); } @@ -1351,7 +1358,6 @@ void SkBitmap::unflatten(SkFlattenableReadBuffer& buffer) { this->setConfig((Config)config, width, height, rowBytes); this->setIsOpaque(buffer.readBool()); - size_t size = this->getSize(); int reftype = buffer.readU8(); switch (reftype) { case SERIALIZE_PIXELTYPE_REF_PTR: { @@ -1373,10 +1379,13 @@ void SkBitmap::unflatten(SkFlattenableReadBuffer& buffer) { if (SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE == reftype) { ctable = SkNEW_ARGS(SkColorTable, (buffer)); } + size_t size = this->getSize(); if (this->allocPixels(ctable)) { this->lockPixels(); - buffer.read(this->getPixels(), this->getSafeSize()); // Just read what we need. - buffer.skip(size - this->getSafeSize()); // Keep aligned for subsequent reads. + // Just read what we need. + buffer.read(this->getPixels(), this->getSafeSize()); + // Keep aligned for subsequent reads. + buffer.skip(size - this->getSafeSize()); this->unlockPixels(); } else { buffer.skip(size); // Still skip the full-sized buffer though. diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp index 6126850..43634ef 100644 --- a/src/core/SkCanvas.cpp +++ b/src/core/SkCanvas.cpp @@ -99,7 +99,7 @@ struct DeviceCM { } void updateMC(const SkMatrix& totalMatrix, const SkRegion& totalClip, - SkRegion* updateClip) { + const SkClipStack& clipStack, SkRegion* updateClip) { int x = fX; int y = fY; int width = fDevice->width(); @@ -126,7 +126,7 @@ struct DeviceCM { SkRegion::kDifference_Op); } - fDevice->setMatrixClip(*fMatrix, fClip); + fDevice->setMatrixClip(*fMatrix, fClip, clipStack); #ifdef SK_DEBUG if (!fClip.isEmpty()) { @@ -477,16 +477,16 @@ SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) { SkDevice* SkCanvas::getDevice() const { // return root device - SkDeque::Iter iter(fMCStack); - MCRec* rec = (MCRec*)iter.next(); + SkDeque::F2BIter iter(fMCStack); + MCRec* rec = (MCRec*)iter.next(); SkASSERT(rec && rec->fLayer); return rec->fLayer->fDevice; } SkDevice* SkCanvas::setDevice(SkDevice* device) { // return root device - SkDeque::Iter iter(fMCStack); - MCRec* rec = (MCRec*)iter.next(); + SkDeque::F2BIter iter(fMCStack); + MCRec* rec = (MCRec*)iter.next(); SkASSERT(rec && rec->fLayer); SkDevice* rootDevice = rec->fLayer->fDevice; @@ -528,6 +528,7 @@ SkDevice* SkCanvas::setDevice(SkDevice* device) { while ((rec = (MCRec*)iter.next()) != NULL) { (void)rec->fRegion->setEmpty(); } + fClipStack.reset(); } else { // compute our total bounds for all devices SkIRect bounds; @@ -539,6 +540,7 @@ SkDevice* SkCanvas::setDevice(SkDevice* device) { while ((rec = (MCRec*)iter.next()) != NULL) { (void)rec->fRegion->op(bounds, SkRegion::kIntersect_Op); } + fClipStack.clipDevRect(bounds, SkRegion::kIntersect_Op); } return device; } @@ -608,7 +610,7 @@ void SkCanvas::updateDeviceCMCache() { DeviceCM* layer = fMCRec->fTopLayer; if (NULL == layer->fNext) { // only one layer - layer->updateMC(totalMatrix, totalClip, NULL); + layer->updateMC(totalMatrix, totalClip, fClipStack, NULL); if (fUseExternalMatrix) { layer->updateExternalMatrix(fExternalMatrix, fExternalInverse); @@ -617,7 +619,7 @@ void SkCanvas::updateDeviceCMCache() { SkRegion clip; clip = totalClip; // make a copy do { - layer->updateMC(totalMatrix, clip, &clip); + layer->updateMC(totalMatrix, clip, fClipStack, &clip); if (fUseExternalMatrix) { layer->updateExternalMatrix(fExternalMatrix, fExternalInverse); @@ -648,6 +650,9 @@ int SkCanvas::internalSave(SaveFlags flags) { newTop->fNext = fMCRec; fMCRec = newTop; + fClipStack.save(); + SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1); + return saveCount; } @@ -729,6 +734,7 @@ int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint, ir = clipBounds; } + fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op); // early exit if the clip is now empty if (bounds_affects_clip(flags) && !fMCRec->fRegion->op(ir, SkRegion::kIntersect_Op)) { @@ -775,6 +781,7 @@ void SkCanvas::internalRestore() { fLocalBoundsCompareTypeDirty = true; fLocalBoundsCompareTypeDirtyBW = true; + fClipStack.restore(); // reserve our layer (if any) DeviceCM* layer = fMCRec->fLayer; // may be null // now detach it from fMCRec so we can pop(). Gets freed after its drawn @@ -798,6 +805,8 @@ void SkCanvas::internalRestore() { } SkDELETE(layer); } + + SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1); } int SkCanvas::getSaveCount() const { @@ -911,6 +920,8 @@ void SkCanvas::resetMatrix() { ////////////////////////////////////////////////////////////////////////////// bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op) { + AutoValidateClip avc(this); + fDeviceCMDirty = true; fLocalBoundsCompareTypeDirty = true; fLocalBoundsCompareTypeDirtyBW = true; @@ -924,6 +935,7 @@ bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op) { SkIRect ir; fMCRec->fMatrix->mapRect(&r, rect); + fClipStack.clipDevRect(r, op); r.round(&ir); return fMCRec->fRegion->op(ir, op); } else { @@ -938,39 +950,84 @@ bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op) { } } -bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op) { - fDeviceCMDirty = true; - fLocalBoundsCompareTypeDirty = true; - fLocalBoundsCompareTypeDirtyBW = true; - - SkPath devPath; - path.transform(*fMCRec->fMatrix, &devPath); - +static bool clipPathHelper(const SkCanvas* canvas, SkRegion* currRgn, + const SkPath& devPath, SkRegion::Op op) { if (SkRegion::kIntersect_Op == op) { - return fMCRec->fRegion->setPath(devPath, *fMCRec->fRegion); + return currRgn->setPath(devPath, *currRgn); } else { SkRegion base; - const SkBitmap& bm = this->getDevice()->accessBitmap(false); + const SkBitmap& bm = canvas->getDevice()->accessBitmap(false); base.setRect(0, 0, bm.width(), bm.height()); if (SkRegion::kReplace_Op == op) { - return fMCRec->fRegion->setPath(devPath, base); + return currRgn->setPath(devPath, base); } else { SkRegion rgn; rgn.setPath(devPath, base); - return fMCRec->fRegion->op(rgn, op); + return currRgn->op(rgn, op); } } } +bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op) { + AutoValidateClip avc(this); + + fDeviceCMDirty = true; + fLocalBoundsCompareTypeDirty = true; + fLocalBoundsCompareTypeDirtyBW = true; + + SkPath devPath; + path.transform(*fMCRec->fMatrix, &devPath); + + // if we called path.swap() we could avoid a deep copy of this path + fClipStack.clipDevPath(devPath, op); + + return clipPathHelper(this, fMCRec->fRegion, devPath, op); +} + bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) { + AutoValidateClip avc(this); + fDeviceCMDirty = true; fLocalBoundsCompareTypeDirty = true; fLocalBoundsCompareTypeDirtyBW = true; + // todo: signal fClipStack that we have a region, and therefore (I guess) + // we have to ignore it, and use the region directly? + fClipStack.clipDevRect(rgn.getBounds()); + return fMCRec->fRegion->op(rgn, op); } +#ifdef SK_DEBUG +void SkCanvas::validateClip() const { + // construct clipRgn from the clipstack + const SkDevice* device = this->getDevice(); + SkIRect ir; + ir.set(0, 0, device->width(), device->height()); + SkRegion clipRgn(ir); + + SkClipStack::B2FIter iter(fClipStack); + const SkClipStack::B2FIter::Clip* clip; + while ((clip = iter.next()) != NULL) { + if (clip->fPath) { + clipPathHelper(this, &clipRgn, *clip->fPath, clip->fOp); + } else if (clip->fRect) { + clip->fRect->round(&ir); + clipRgn.op(ir, clip->fOp); + } else { + break; + } + } + + // now compare against the current rgn + const SkRegion& rgn = this->getTotalClip(); + SkASSERT(rgn == clipRgn); +} +#endif + +/////////////////////////////////////////////////////////////////////////////// + void SkCanvas::computeLocalClipBoundsCompareType(EdgeType et) const { SkRect r; SkRectCompareType& rCompare = et == kAA_EdgeType ? fLocalBoundsCompareType : diff --git a/src/core/SkClipStack.cpp b/src/core/SkClipStack.cpp new file mode 100644 index 0000000..2b63aea --- /dev/null +++ b/src/core/SkClipStack.cpp @@ -0,0 +1,144 @@ +#include "SkClipStack.h" +#include "SkPath.h" +#include <new> + +struct SkClipStack::Rec { + enum State { + kEmpty_State, + kRect_State, + kPath_State + }; + + SkPath fPath; + SkRect fRect; + int fSaveCount; + SkRegion::Op fOp; + State fState; + + Rec(int saveCount, const SkRect& rect, SkRegion::Op op) : fRect(rect) { + fSaveCount = saveCount; + fOp = op; + fState = kRect_State; + } + + Rec(int saveCount, const SkPath& path, SkRegion::Op op) : fPath(path) { + fSaveCount = saveCount; + fOp = op; + fState = kPath_State; + } + + /** + * Returns true if this Rec can be intersected in place with a new clip + */ + bool canBeIntersected(int saveCount, SkRegion::Op op) const { + if (kEmpty_State == fState) { + return true; + } + return fSaveCount == saveCount && + SkRegion::kIntersect_Op == fOp && + SkRegion::kIntersect_Op == op; + } +}; + +SkClipStack::SkClipStack() : fDeque(sizeof(Rec)) { + fSaveCount = 0; +} + +void SkClipStack::reset() { + // don't have a reset() on SkDeque, so fake it here + fDeque.~SkDeque(); + new (&fDeque) SkDeque(sizeof(Rec)); + + fSaveCount = 0; +} + +void SkClipStack::save() { + fSaveCount += 1; +} + +void SkClipStack::restore() { + fSaveCount -= 1; + while (!fDeque.empty()) { + Rec* rec = (Rec*)fDeque.back(); + if (rec->fSaveCount <= fSaveCount) { + break; + } + rec->~Rec(); + fDeque.pop_back(); + } +} + +void SkClipStack::clipDevRect(const SkRect& rect, SkRegion::Op op) { + Rec* rec = (Rec*)fDeque.back(); + if (rec && rec->canBeIntersected(fSaveCount, op)) { + switch (rec->fState) { + case Rec::kEmpty_State: + return; + case Rec::kRect_State: + if (!rec->fRect.intersect(rect)) { + rec->fState = Rec::kEmpty_State; + } + return; + case Rec::kPath_State: + if (!SkRect::Intersects(rec->fPath.getBounds(), rect)) { + rec->fState = Rec::kEmpty_State; + return; + } + break; + } + } + new (fDeque.push_back()) Rec(fSaveCount, rect, op); +} + +void SkClipStack::clipDevPath(const SkPath& path, SkRegion::Op op) { + Rec* rec = (Rec*)fDeque.back(); + if (rec && rec->canBeIntersected(fSaveCount, op)) { + const SkRect& pathBounds = path.getBounds(); + switch (rec->fState) { + case Rec::kEmpty_State: + return; + case Rec::kRect_State: + if (!SkRect::Intersects(rec->fRect, pathBounds)) { + rec->fState = Rec::kEmpty_State; + return; + } + break; + case Rec::kPath_State: + if (!SkRect::Intersects(rec->fPath.getBounds(), pathBounds)) { + rec->fState = Rec::kEmpty_State; + return; + } + break; + } + } + new (fDeque.push_back()) Rec(fSaveCount, path, op); +} + +/////////////////////////////////////////////////////////////////////////////// + +SkClipStack::B2FIter::B2FIter(const SkClipStack& stack) : fIter(stack.fDeque) { +} + +const SkClipStack::B2FIter::Clip* SkClipStack::B2FIter::next() { + const SkClipStack::Rec* rec = (const SkClipStack::Rec*)fIter.next(); + if (NULL == rec) { + return NULL; + } + + switch (rec->fState) { + case SkClipStack::Rec::kEmpty_State: + fClip.fRect = NULL; + fClip.fPath = NULL; + break; + case SkClipStack::Rec::kRect_State: + fClip.fRect = &rec->fRect; + fClip.fPath = NULL; + break; + case SkClipStack::Rec::kPath_State: + fClip.fRect = NULL; + fClip.fPath = &rec->fPath; + break; + } + fClip.fOp = rec->fOp; + return &fClip; +} diff --git a/src/core/SkDeque.cpp b/src/core/SkDeque.cpp index 4f15051..2c6ce64 100644 --- a/src/core/SkDeque.cpp +++ b/src/core/SkDeque.cpp @@ -2,16 +2,16 @@ ** ** Copyright 2006, The Android Open Source Project ** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at ** -** http://www.apache.org/licenses/LICENSE-2.0 +** http://www.apache.org/licenses/LICENSE-2.0 ** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and ** limitations under the License. */ @@ -25,10 +25,10 @@ struct SkDeque::Head { char* fBegin; // start of used section in this chunk char* fEnd; // end of used section in this chunk char* fStop; // end of the allocated chunk - + char* start() { return (char*)(this + 1); } const char* start() const { return (const char*)(this + 1); } - + void init(size_t size) { fNext = fPrev = NULL; fBegin = fEnd = NULL; @@ -44,7 +44,7 @@ SkDeque::SkDeque(size_t elemSize) SkDeque::SkDeque(size_t elemSize, void* storage, size_t storageSize) : fElemSize(elemSize), fInitialStorage(storage), fCount(0) { SkASSERT(storageSize == 0 || storage != NULL); - + if (storageSize >= sizeof(Head) + elemSize) { fFront = (Head*)storage; fFront->init(storageSize); @@ -69,7 +69,7 @@ SkDeque::~SkDeque() { const void* SkDeque::front() const { Head* front = fFront; - + if (NULL == front) { return NULL; } @@ -108,7 +108,7 @@ void* SkDeque::push_front() { fFront->init(sizeof(Head) + INIT_ELEM_COUNT * fElemSize); fBack = fFront; // update our linklist } - + Head* first = fFront; char* begin; @@ -144,7 +144,7 @@ void* SkDeque::push_back() { fBack->init(sizeof(Head) + INIT_ELEM_COUNT * fElemSize); fFront = fBack; // update our linklist } - + Head* last = fBack; char* end; @@ -178,7 +178,7 @@ void SkDeque::pop_front() { Head* first = fFront; SkASSERT(first != NULL); - + if (first->fBegin == NULL) { // we were marked empty from before first = first->fNext; first->fPrev = NULL; @@ -202,9 +202,9 @@ void SkDeque::pop_back() { fCount -= 1; Head* last = fBack; - + SkASSERT(last != NULL); - + if (last->fEnd == NULL) { // we were marked empty from before last = last->fPrev; last->fNext = NULL; @@ -212,7 +212,7 @@ void SkDeque::pop_back() { fBack = last; SkASSERT(last != NULL); // else we popped too far } - + char* end = last->fEnd - fElemSize; SkASSERT(end >= last->fBegin); @@ -225,7 +225,7 @@ void SkDeque::pop_back() { /////////////////////////////////////////////////////////////////////////////// -SkDeque::Iter::Iter(const SkDeque& d) : fElemSize(d.fElemSize) { +SkDeque::F2BIter::F2BIter(const SkDeque& d) : fElemSize(d.fElemSize) { fHead = d.fFront; while (fHead != NULL && fHead->fBegin == NULL) { fHead = fHead->fNext; @@ -233,9 +233,9 @@ SkDeque::Iter::Iter(const SkDeque& d) : fElemSize(d.fElemSize) { fPos = fHead ? fHead->fBegin : NULL; } -void* SkDeque::Iter::next() { +void* SkDeque::F2BIter::next() { char* pos = fPos; - + if (pos) { // if we were valid, try to move to the next setting char* next = pos + fElemSize; SkASSERT(next <= fHead->fEnd); diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp index a270677..2921be0 100644 --- a/src/core/SkDevice.cpp +++ b/src/core/SkDevice.cpp @@ -11,7 +11,7 @@ SkDevice::SkDevice(SkCanvas* canvas, const SkBitmap& bitmap, bool isForLayer) // auto-allocate if we're for offscreen drawing if (isForLayer) { if (NULL == fBitmap.getPixels() && NULL == fBitmap.pixelRef()) { - fBitmap.allocPixels(); + fBitmap.allocPixels(); if (!fBitmap.isOpaque()) { fBitmap.eraseColor(0); } @@ -43,7 +43,7 @@ void SkDevice::getBounds(SkIRect* bounds) const { bool SkDevice::intersects(const SkIRect& r, SkIRect* sect) const { SkIRect bounds; - + this->getBounds(&bounds); return sect ? sect->intersect(r, bounds) : SkIRect::Intersects(r, bounds); } @@ -54,7 +54,8 @@ void SkDevice::eraseColor(SkColor eraseColor) { void SkDevice::onAccessBitmap(SkBitmap* bitmap) {} -void SkDevice::setMatrixClip(const SkMatrix&, const SkRegion&) {} +void SkDevice::setMatrixClip(const SkMatrix&, const SkRegion&, + const SkClipStack&) {} /////////////////////////////////////////////////////////////////////////////// @@ -116,7 +117,7 @@ void SkDevice::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap, const SkMatrix& matrix, const SkPaint& paint) { SkBitmap tmp; // storage if we need a subset of bitmap const SkBitmap* bitmapPtr = &bitmap; - + if (srcRect) { if (!bitmap.extractSubset(&tmp, *srcRect)) { return; // extraction failed diff --git a/src/core/SkScalerContext.cpp b/src/core/SkScalerContext.cpp index f98969c..697917a 100644 --- a/src/core/SkScalerContext.cpp +++ b/src/core/SkScalerContext.cpp @@ -383,7 +383,7 @@ void SkScalerContext::getImage(const SkGlyph& origGlyph) { SkGlyph tmpGlyph; if (fMaskFilter) { // restore the prefilter bounds - tmpGlyph.fID = origGlyph.fID; + tmpGlyph.init(origGlyph.fID); // need the original bounds, sans our maskfilter SkMaskFilter* mf = fMaskFilter; diff --git a/src/effects/SkBlurDrawLooper.cpp b/src/effects/SkBlurDrawLooper.cpp index a657780..901a4d3 100644 --- a/src/effects/SkBlurDrawLooper.cpp +++ b/src/effects/SkBlurDrawLooper.cpp @@ -3,6 +3,7 @@ #include "SkCanvas.h" #include "SkPaint.h" #include "SkMaskFilter.h" +#include "SkColorFilter.h" SkBlurDrawLooper::SkBlurDrawLooper(SkScalar radius, SkScalar dx, SkScalar dy, SkColor color, uint32_t flags) @@ -15,12 +16,28 @@ SkBlurDrawLooper::SkBlurDrawLooper(SkScalar radius, SkScalar dx, SkScalar dy, SkBlurMaskFilter::kIgnoreTransform_BlurFlag : SkBlurMaskFilter::kNone_BlurFlag; + blurFlags |= flags & kHighQuality_BlurFlag ? + SkBlurMaskFilter::kHighQuality_BlurFlag : + SkBlurMaskFilter::kNone_BlurFlag; + fBlur = SkBlurMaskFilter::Create(radius, - SkBlurMaskFilter::kNormal_BlurStyle, + SkBlurMaskFilter::kNormal_BlurStyle, blurFlags); } else + { fBlur = NULL; + } + + if (flags & kOverrideColor_BlurFlag) + { + //The SrcIn xfer mode will multiply 'color' by the incoming alpha + fColorFilter = SkColorFilter::CreateModeFilter(color, SkXfermode::kSrcIn_Mode); + } + else + { + fColorFilter = NULL; + } } SkBlurDrawLooper::SkBlurDrawLooper(SkFlattenableReadBuffer& buffer) @@ -29,12 +46,14 @@ SkBlurDrawLooper::SkBlurDrawLooper(SkFlattenableReadBuffer& buffer) fDy = buffer.readScalar(); fBlurColor = buffer.readU32(); fBlur = static_cast<SkMaskFilter*>(buffer.readFlattenable()); + fColorFilter = static_cast<SkColorFilter*>(buffer.readFlattenable()); fBlurFlags = buffer.readU32() & kAll_BlurFlag; } SkBlurDrawLooper::~SkBlurDrawLooper() { SkSafeUnref(fBlur); + SkSafeUnref(fColorFilter); } void SkBlurDrawLooper::flatten(SkFlattenableWriteBuffer& buffer) @@ -43,6 +62,7 @@ void SkBlurDrawLooper::flatten(SkFlattenableWriteBuffer& buffer) buffer.writeScalar(fDy); buffer.write32(fBlurColor); buffer.writeFlattenable(fBlur); + buffer.writeFlattenable(fColorFilter); buffer.write32(fBlurFlags); } @@ -74,6 +94,7 @@ bool SkBlurDrawLooper::next() } fPaint->setColor(blurColor); fPaint->setMaskFilter(fBlur); + fPaint->setColorFilter(fColorFilter); fCanvas->save(SkCanvas::kMatrix_SaveFlag); if (fBlurFlags & kIgnoreTransform_BlurFlag) { @@ -90,6 +111,7 @@ bool SkBlurDrawLooper::next() case kAfterEdge: fPaint->setColor(fSavedColor); fPaint->setMaskFilter(NULL); + fPaint->setColorFilter(NULL); fCanvas->restore(); // to remove the translate we did earlier fState = kDone; return true; @@ -105,6 +127,7 @@ void SkBlurDrawLooper::restore() { fPaint->setColor(fSavedColor); fPaint->setMaskFilter(NULL); + fPaint->setColorFilter(NULL); fCanvas->restore(); // to remove the translate we did earlier fState = kDone; } diff --git a/src/effects/SkBlurMask.cpp b/src/effects/SkBlurMask.cpp index a6492c3..f57a177 100644 --- a/src/effects/SkBlurMask.cpp +++ b/src/effects/SkBlurMask.cpp @@ -246,13 +246,20 @@ void SkMask_FreeImage(uint8_t* image) } bool SkBlurMask::Blur(SkMask* dst, const SkMask& src, - SkScalar radius, Style style) + SkScalar radius, Style style, Quality quality) { if (src.fFormat != SkMask::kA8_Format) return false; - int rx = SkScalarCeil(radius); - int outer_weight = 255 - SkScalarRound((SkIntToScalar(rx) - radius) * 255); + // Force high quality off for small radii (performance) + if (radius < SkIntToScalar(3)) quality = kLow_Quality; + + // highQuality: use three box blur passes as a cheap way to approximate a Gaussian blur + int passCount = (quality == kHigh_Quality) ? 3 : 1; + SkScalar passRadius = SkScalarDiv(radius, SkScalarSqrt(SkIntToScalar(passCount))); + + int rx = SkScalarCeil(passRadius); + int outer_weight = 255 - SkScalarRound((SkIntToScalar(rx) - passRadius) * 255); SkASSERT(rx >= 0); SkASSERT((unsigned)outer_weight <= 255); @@ -262,8 +269,10 @@ bool SkBlurMask::Blur(SkMask* dst, const SkMask& src, int ry = rx; // only do square blur for now - dst->fBounds.set(src.fBounds.fLeft - rx, src.fBounds.fTop - ry, - src.fBounds.fRight + rx, src.fBounds.fBottom + ry); + int padx = passCount * rx; + int pady = passCount * ry; + dst->fBounds.set(src.fBounds.fLeft - padx, src.fBounds.fTop - pady, + src.fBounds.fRight + padx, src.fBounds.fBottom + pady); dst->fRowBytes = dst->fBounds.width(); dst->fFormat = SkMask::kA8_Format; dst->fImage = NULL; @@ -283,15 +292,38 @@ bool SkBlurMask::Blur(SkMask* dst, const SkMask& src, // build the blurry destination { - SkAutoTMalloc<uint32_t> storage((sw + 1) * (sh + 1)); + SkAutoTMalloc<uint32_t> storage((sw + 2 * (passCount - 1) * rx + 1) * (sh + 2 * (passCount - 1) * ry + 1)); uint32_t* sumBuffer = storage.get(); + //pass1: sp is source, dp is destination build_sum_buffer(sumBuffer, sw, sh, sp, src.fRowBytes); dump_sum_buffer(sumBuffer, sw, sh); if (outer_weight == 255) apply_kernel(dp, rx, ry, sumBuffer, sw, sh); else apply_kernel_interp(dp, rx, ry, sumBuffer, sw, sh, outer_weight); + + if (quality == kHigh_Quality) + { + //pass2: dp is source, tmpBuffer is destination + int tmp_sw = sw + 2 * rx; + int tmp_sh = sh + 2 * ry; + SkAutoTMalloc<uint8_t> tmpBuffer(dstSize); + build_sum_buffer(sumBuffer, tmp_sw, tmp_sh, dp, tmp_sw); + if (outer_weight == 255) + apply_kernel(tmpBuffer.get(), rx, ry, sumBuffer, tmp_sw, tmp_sh); + else + apply_kernel_interp(tmpBuffer.get(), rx, ry, sumBuffer, tmp_sw, tmp_sh, outer_weight); + + //pass3: tmpBuffer is source, dp is destination + tmp_sw += 2 * rx; + tmp_sh += 2 * ry; + build_sum_buffer(sumBuffer, tmp_sw, tmp_sh, tmpBuffer.get(), tmp_sw); + if (outer_weight == 255) + apply_kernel(dp, rx, ry, sumBuffer, tmp_sw, tmp_sh); + else + apply_kernel_interp(dp, rx, ry, sumBuffer, tmp_sw, tmp_sh, outer_weight); + } } dst->fImage = dp; @@ -306,11 +338,11 @@ bool SkBlurMask::Blur(SkMask* dst, const SkMask& src, dst->fImage = SkMask::AllocImage(srcSize); merge_src_with_blur(dst->fImage, src.fRowBytes, sp, src.fRowBytes, - dp + rx + ry*dst->fRowBytes, dst->fRowBytes, + dp + passCount * (rx + ry * dst->fRowBytes), dst->fRowBytes, sw, sh); SkMask::FreeImage(dp); } else if (style != kNormal_Style) { - clamp_with_orig(dp + rx + ry*dst->fRowBytes, dst->fRowBytes, + clamp_with_orig(dp + passCount * (rx + ry * dst->fRowBytes), dst->fRowBytes, sp, src.fRowBytes, sw, sh, style); } diff --git a/src/effects/SkBlurMask.h b/src/effects/SkBlurMask.h index 8f61d54..560ef48 100644 --- a/src/effects/SkBlurMask.h +++ b/src/effects/SkBlurMask.h @@ -31,7 +31,12 @@ public: kStyleCount }; - static bool Blur(SkMask* dst, const SkMask& src, SkScalar radius, Style); + enum Quality { + kLow_Quality, //!< box blur + kHigh_Quality //!< three pass box blur (similar to gaussian) + }; + + static bool Blur(SkMask* dst, const SkMask& src, SkScalar radius, Style, Quality quality); }; #endif diff --git a/src/effects/SkBlurMaskFilter.cpp b/src/effects/SkBlurMaskFilter.cpp index 8941cd1..41e04b8 100644 --- a/src/effects/SkBlurMaskFilter.cpp +++ b/src/effects/SkBlurMaskFilter.cpp @@ -96,8 +96,10 @@ bool SkBlurMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src, const SkMa // a request like 10,000) static const SkScalar MAX_RADIUS = SkIntToScalar(128); radius = SkMinScalar(radius, MAX_RADIUS); + SkBlurMask::Quality blurQuality = (fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag) ? + SkBlurMask::kHigh_Quality : SkBlurMask::kLow_Quality; - if (SkBlurMask::Blur(dst, src, radius, (SkBlurMask::Style)fBlurStyle)) + if (SkBlurMask::Blur(dst, src, radius, (SkBlurMask::Style)fBlurStyle, blurQuality)) { if (margin) { // we need to integralize radius for our margin, so take the ceil diff --git a/src/effects/SkEmbossMaskFilter.cpp b/src/effects/SkEmbossMaskFilter.cpp index 67c8024..9d585ff 100644 --- a/src/effects/SkEmbossMaskFilter.cpp +++ b/src/effects/SkEmbossMaskFilter.cpp @@ -73,7 +73,7 @@ bool SkEmbossMaskFilter::filterMask(SkMask* dst, const SkMask& src, const SkMatr { SkScalar radius = matrix.mapRadius(fBlurRadius); - if (!SkBlurMask::Blur(dst, src, radius, SkBlurMask::kInner_Style)) + if (!SkBlurMask::Blur(dst, src, radius, SkBlurMask::kInner_Style, SkBlurMask::kLow_Quality)) return false; dst->fFormat = SkMask::k3D_Format; diff --git a/src/effects/SkLayerRasterizer.cpp b/src/effects/SkLayerRasterizer.cpp index 168fbe9..851f418 100644 --- a/src/effects/SkLayerRasterizer.cpp +++ b/src/effects/SkLayerRasterizer.cpp @@ -37,7 +37,7 @@ SkLayerRasterizer::SkLayerRasterizer() : fLayers(sizeof(SkLayerRasterizer_Rec)) SkLayerRasterizer::~SkLayerRasterizer() { - SkDeque::Iter iter(fLayers); + SkDeque::F2BIter iter(fLayers); SkLayerRasterizer_Rec* rec; while ((rec = (SkLayerRasterizer_Rec*)iter.next()) != NULL) @@ -55,7 +55,7 @@ void SkLayerRasterizer::addLayer(const SkPaint& paint, SkScalar dx, SkScalar dy) static bool compute_bounds(const SkDeque& layers, const SkPath& path, const SkMatrix& matrix, const SkIRect* clipBounds, SkIRect* bounds) { - SkDeque::Iter iter(layers); + SkDeque::F2BIter iter(layers); SkLayerRasterizer_Rec* rec; bounds->set(SK_MaxS32, SK_MaxS32, SK_MinS32, SK_MinS32); @@ -139,7 +139,7 @@ bool SkLayerRasterizer::onRasterize(const SkPath& path, const SkMatrix& matrix, // we set the matrixproc in the loop, as the matrix changes each time (potentially) draw.fBounder = NULL; - SkDeque::Iter iter(fLayers); + SkDeque::F2BIter iter(fLayers); SkLayerRasterizer_Rec* rec; while ((rec = (SkLayerRasterizer_Rec*)iter.next()) != NULL) { @@ -219,7 +219,7 @@ void SkLayerRasterizer::flatten(SkFlattenableWriteBuffer& buffer) buffer.write32(fLayers.count()); - SkDeque::Iter iter(fLayers); + SkDeque::F2BIter iter(fLayers); const SkLayerRasterizer_Rec* rec; while ((rec = (const SkLayerRasterizer_Rec*)iter.next()) != NULL) diff --git a/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp index a216708..043f0df 100644 --- a/src/ports/SkFontHost_FreeType.cpp +++ b/src/ports/SkFontHost_FreeType.cpp @@ -522,7 +522,7 @@ void SkFontHost::FilterRec(SkScalerContext::Rec* rec) { InitFreetype(); FT_Done_FreeType(gFTLibrary); } - + if (!gLCDSupport && rec->isLCD()) { // If the runtime Freetype library doesn't support LCD mode, we disable // it here. @@ -1261,11 +1261,12 @@ SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) { /* Export this so that other parts of our FonttHost port can make use of our ability to extract the name+style from a stream, using FreeType's api. */ -bool find_name_and_style(SkStream* stream, SkString* name, SkTypeface::Style* style) { +SkTypeface::Style find_name_and_attributes(SkStream* stream, SkString* name, + bool* isFixedWidth) { FT_Library library; if (FT_Init_FreeType(&library)) { - name->set(NULL); - return false; + name->reset(); + return SkTypeface::kNormal; } FT_Open_Args args; @@ -1292,22 +1293,24 @@ bool find_name_and_style(SkStream* stream, SkString* name, SkTypeface::Style* st FT_Face face; if (FT_Open_Face(library, &args, 0, &face)) { FT_Done_FreeType(library); - name->set(NULL); - return false; + name->reset(); + return SkTypeface::kNormal; } name->set(face->family_name); - int tempStyle = SkTypeface::kNormal; + int style = SkTypeface::kNormal; if (face->style_flags & FT_STYLE_FLAG_BOLD) { - tempStyle |= SkTypeface::kBold; + style |= SkTypeface::kBold; } if (face->style_flags & FT_STYLE_FLAG_ITALIC) { - tempStyle |= SkTypeface::kItalic; + style |= SkTypeface::kItalic; + } + if (isFixedWidth) { + *isFixedWidth = FT_IS_FIXED_WIDTH(face); } - *style = (SkTypeface::Style)tempStyle; FT_Done_Face(face); FT_Done_FreeType(library); - return true; + return (SkTypeface::Style)style; } diff --git a/src/ports/SkFontHost_android.cpp b/src/ports/SkFontHost_android.cpp index 7e515d0..d01577d 100644 --- a/src/ports/SkFontHost_android.cpp +++ b/src/ports/SkFontHost_android.cpp @@ -31,7 +31,8 @@ #define SK_FONT_FILE_PREFIX "/fonts/" #endif -bool find_name_and_style(SkStream* stream, SkString* name, SkTypeface::Style* style); +SkTypeface::Style find_name_and_attributes(SkStream* stream, SkString* name, + bool* isFixedWidth); static void GetFullPathForSysFonts(SkString* full, const char name[]) { full->set(getenv("ANDROID_ROOT")); @@ -235,8 +236,9 @@ static void remove_from_names(FamilyRec* emptyFamily) class FamilyTypeface : public SkTypeface { public: - FamilyTypeface(Style style, bool sysFont, SkTypeface* familyMember) - : SkTypeface(style, sk_atomic_inc(&gUniqueFontID) + 1) { + FamilyTypeface(Style style, bool sysFont, SkTypeface* familyMember, + bool isFixedWidth) + : SkTypeface(style, sk_atomic_inc(&gUniqueFontID) + 1, isFixedWidth) { fIsSysFont = sysFont; SkAutoMutexAcquire ac(gFamilyMutex); @@ -280,8 +282,8 @@ private: class StreamTypeface : public FamilyTypeface { public: StreamTypeface(Style style, bool sysFont, SkTypeface* familyMember, - SkStream* stream) - : INHERITED(style, sysFont, familyMember) { + SkStream* stream, bool isFixedWidth) + : INHERITED(style, sysFont, familyMember, isFixedWidth) { SkASSERT(stream); stream->ref(); fStream = stream; @@ -311,8 +313,8 @@ private: class FileTypeface : public FamilyTypeface { public: FileTypeface(Style style, bool sysFont, SkTypeface* familyMember, - const char path[]) - : INHERITED(style, sysFont, familyMember) { + const char path[], bool isFixedWidth) + : INHERITED(style, sysFont, familyMember, isFixedWidth) { SkString fullpath; if (sysFont) { @@ -359,18 +361,21 @@ private: /////////////////////////////////////////////////////////////////////////////// static bool get_name_and_style(const char path[], SkString* name, - SkTypeface::Style* style, bool isExpected) { + SkTypeface::Style* style, + bool* isFixedWidth, bool isExpected) { SkString fullpath; GetFullPathForSysFonts(&fullpath, path); SkMMAPStream stream(fullpath.c_str()); if (stream.getLength() > 0) { - return find_name_and_style(&stream, name, style); + *style = find_name_and_attributes(&stream, name, isFixedWidth); + return true; } else { SkFILEStream stream(fullpath.c_str()); if (stream.getLength() > 0) { - return find_name_and_style(&stream, name, style); + *style = find_name_and_attributes(&stream, name, isFixedWidth); + return true; } } @@ -461,12 +466,14 @@ static void load_system_fonts() { firstInFamily = NULL; } + bool isFixedWidth; SkString name; SkTypeface::Style style; // we expect all the fonts, except the "fallback" fonts bool isExpected = (rec[i].fNames != gFBNames); - if (!get_name_and_style(rec[i].fFileName, &name, &style, isExpected)) { + if (!get_name_and_style(rec[i].fFileName, &name, &style, + &isFixedWidth, isExpected)) { continue; } @@ -474,7 +481,8 @@ static void load_system_fonts() { (style, true, // system-font (cannot delete) firstInFamily, // what family to join - rec[i].fFileName) // filename + rec[i].fFileName, + isFixedWidth) // filename ); if (rec[i].fNames != NULL) { @@ -648,11 +656,12 @@ SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) { return NULL; } + bool isFixedWidth; SkString name; - SkTypeface::Style style; + SkTypeface::Style style = find_name_and_attributes(stream, &name, &isFixedWidth); - if (find_name_and_style(stream, &name, &style)) { - return SkNEW_ARGS(StreamTypeface, (style, false, NULL, stream)); + if (!name.isEmpty()) { + return SkNEW_ARGS(StreamTypeface, (style, false, NULL, stream, isFixedWidth)); } else { return NULL; } diff --git a/src/ports/SkFontHost_linux.cpp b/src/ports/SkFontHost_linux.cpp index e85dff4..9aabce7 100644 --- a/src/ports/SkFontHost_linux.cpp +++ b/src/ports/SkFontHost_linux.cpp @@ -32,7 +32,8 @@ #define SK_FONT_FILE_PREFIX "/usr/share/fonts/truetype/msttcorefonts/" #endif -SkTypeface::Style find_name_and_style(SkStream* stream, SkString* name); +SkTypeface::Style find_name_and_attributes(SkStream* stream, SkString* name, + bool* isFixedWidth); static void GetFullPathForSysFonts(SkString* full, const char name[]) { @@ -237,8 +238,8 @@ static void remove_from_names(FamilyRec* emptyFamily) { class FamilyTypeface : public SkTypeface { public: - FamilyTypeface(Style style, bool sysFont, FamilyRec* family) - : SkTypeface(style, sk_atomic_inc(&gUniqueFontID) + 1) { + FamilyTypeface(Style style, bool sysFont, FamilyRec* family, bool isFixedWidth) + : SkTypeface(style, sk_atomic_inc(&gUniqueFontID) + 1, isFixedWidth) { fIsSysFont = sysFont; SkAutoMutexAcquire ac(gFamilyMutex); @@ -283,7 +284,7 @@ private: */ class EmptyTypeface : public FamilyTypeface { public: - EmptyTypeface() : INHERITED(SkTypeface::kNormal, true, NULL) {} + EmptyTypeface() : INHERITED(SkTypeface::kNormal, true, NULL, false) {} // overrides virtual SkStream* openStream() { return NULL; } @@ -296,8 +297,8 @@ private: class StreamTypeface : public FamilyTypeface { public: StreamTypeface(Style style, bool sysFont, FamilyRec* family, - SkStream* stream) - : INHERITED(style, sysFont, family) { + SkStream* stream, bool isFixedWidth) + : INHERITED(style, sysFont, family, isFixedWidth) { stream->ref(); fStream = stream; } @@ -323,8 +324,8 @@ private: class FileTypeface : public FamilyTypeface { public: FileTypeface(Style style, bool sysFont, FamilyRec* family, - const char path[]) - : INHERITED(style, sysFont, family) { + const char path[], bool isFixedWidth) + : INHERITED(style, sysFont, family, isFixedWidth) { fPath.set(path); } @@ -364,16 +365,16 @@ private: /////////////////////////////////////////////////////////////////////////////// static bool get_name_and_style(const char path[], SkString* name, - SkTypeface::Style* style) { + SkTypeface::Style* style, bool* isFixedWidth) { SkMMAPStream stream(path); if (stream.getLength() > 0) { - *style = find_name_and_style(&stream, name); + *style = find_name_and_attributes(&stream, name, isFixedWidth); return true; } else { SkFILEStream stream(path); if (stream.getLength() > 0) { - *style = find_name_and_style(&stream, name); + *style = find_name_and_attributes(&stream, name, isFixedWidth); return true; } } @@ -402,10 +403,11 @@ static void load_system_fonts() { SkString filename; GetFullPathForSysFonts(&filename, name.c_str()); + bool isFixedWidth; SkString realname; SkTypeface::Style style = SkTypeface::kNormal; // avoid uninitialized warning - if (!get_name_and_style(filename.c_str(), &realname, &style)) { + if (!get_name_and_style(filename.c_str(), &realname, &style, &isFixedWidth)) { SkDebugf("------ can't load <%s> as a font\n", filename.c_str()); continue; } @@ -424,7 +426,8 @@ static void load_system_fonts() { (style, true, // system-font (cannot delete) family, // what family to join - filename.c_str()) // filename + filename.c_str(), + isFixedWidth) // filename ); if (NULL == family) { @@ -586,11 +589,12 @@ SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) { SkDELETE(stream); return NULL; } - + + bool isFixedWidth; SkString name; - SkTypeface::Style style = find_name_and_style(stream, &name); + SkTypeface::Style style = find_name_and_attributes(stream, &name, &isFixedWidth); - return SkNEW_ARGS(StreamTypeface, (style, false, NULL, stream)); + return SkNEW_ARGS(StreamTypeface, (style, false, NULL, stream, isFixedWidth)); } SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) { diff --git a/src/ports/SkFontHost_win.cpp b/src/ports/SkFontHost_win.cpp index c2e43fa..c2055aa 100644 --- a/src/ports/SkFontHost_win.cpp +++ b/src/ports/SkFontHost_win.cpp @@ -22,10 +22,12 @@ #include "SkAdvancedTypefaceMetrics.h"
#include "SkStream.h"
#include "SkThread.h"
+#include "SkUtils.h"
#ifdef WIN32
#include "windows.h"
#include "tchar.h"
+#include "Usp10.h"
// client3d has to undefine this for now
#define CAN_USE_LOGFONT_NAME
@@ -179,14 +181,16 @@ protected: virtual void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY);
//virtual SkDeviceContext getDC() {return ddc;}
private:
- LOGFONT lf;
- MAT2 mat22;
- HDC ddc;
- HFONT savefont;
- HFONT font;
+ LOGFONT lf;
+ MAT2 mat22;
+ HDC ddc;
+ HFONT savefont;
+ HFONT font;
+ SCRIPT_CACHE sc;
};
-SkScalerContext_Windows::SkScalerContext_Windows(const SkDescriptor* desc) : SkScalerContext(desc), ddc(0), font(0), savefont(0) {
+SkScalerContext_Windows::SkScalerContext_Windows(const SkDescriptor* desc)
+ : SkScalerContext(desc), ddc(0), font(0), savefont(0), sc(0) {
SkAutoMutexAcquire ac(gFTMutex);
lf = LogFontTypeface::FindById(fRec.fFontID)->logFont();
@@ -216,6 +220,9 @@ SkScalerContext_Windows::~SkScalerContext_Windows() { if (font) {
::DeleteObject(font);
}
+ if (sc) {
+ ::ScriptFreeCache(&sc);
+ }
}
unsigned SkScalerContext_Windows::generateGlyphCount() const {
@@ -225,9 +232,26 @@ unsigned SkScalerContext_Windows::generateGlyphCount() const { uint16_t SkScalerContext_Windows::generateCharToGlyph(SkUnichar uni) {
uint16_t index = 0;
- // TODO(ctguil): Support values larger than 16bits.
- WCHAR c = SkToU16(uni);
- GetGlyphIndicesW(ddc, &c, 1, &index, 0);
+ WCHAR c[2];
+ // TODO(ctguil): Support characters that generate more than one glyph.
+ if (SkUTF16_FromUnichar(uni, (uint16_t*)c) == 1) {
+ // Type1 fonts fail with uniscribe API. Use GetGlyphIndices for plane 0.
+ SkAssertResult(GetGlyphIndicesW(ddc, c, 1, &index, 0));
+ } else {
+ // Use uniscribe to detemine glyph index for non-BMP characters.
+ // Need to add extra item to SCRIPT_ITEM to work around a bug in older
+ // windows versions. https://bugzilla.mozilla.org/show_bug.cgi?id=366643
+ SCRIPT_ITEM si[2 + 1];
+ int items;
+ SkAssertResult(
+ SUCCEEDED(ScriptItemize(c, 2, 2, NULL, NULL, si, &items)));
+
+ WORD log[2];
+ SCRIPT_VISATTR vsa;
+ int glyphs;
+ SkAssertResult(SUCCEEDED(ScriptShape(
+ ddc, &sc, c, 2, 1, &si[0].a, &index, log, &vsa, &glyphs)));
+ }
return index;
}
@@ -524,6 +548,7 @@ SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics( }
info->fEmSize = otm.otmEMSquare;
info->fMultiMaster = false;
+ info->fLastGlyphID = 0;
info->fStyle = 0;
// If this bit is clear the font is a fixed pitch font.
@@ -578,6 +603,15 @@ SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics( } else if (perGlyphInfo) {
info->fGlyphWidths.reset(
getAdvanceData(hdc, SHRT_MAX, &getWidthAdvance));
+
+ // Obtain the last glyph index.
+ SkAdvancedTypefaceMetrics::WidthRange* last = info->fGlyphWidths.get();
+ if (last) {
+ while (last->fNext.get()) {
+ last = last->fNext.get();
+ }
+ info->fLastGlyphID = last->fEndId;
+ }
}
Error:
|