diff options
Diffstat (limited to 'src')
-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 |
16 files changed, 440 insertions, 117 deletions
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:
|