diff options
Diffstat (limited to 'libs/graphics/sgl/SkDraw.cpp')
-rw-r--r-- | libs/graphics/sgl/SkDraw.cpp | 1139 |
1 files changed, 1139 insertions, 0 deletions
diff --git a/libs/graphics/sgl/SkDraw.cpp b/libs/graphics/sgl/SkDraw.cpp new file mode 100644 index 0000000000..9b1ed985ef --- /dev/null +++ b/libs/graphics/sgl/SkDraw.cpp @@ -0,0 +1,1139 @@ +#include "SkDraw.h" +#include "SkBlitter.h" +#include "SkBounder.h" +#include "SkCanvas.h" +#include "SkMaskFilter.h" +#include "SkPaint.h" +#include "SkPathEffect.h" +#include "SkRasterizer.h" +#include "SkScan.h" +#include "SkShader.h" +#include "SkStroke.h" +#include "SkTemplatesPriv.h" +#include "SkTextLayout.h" + +/** Helper for allocating small blitters on the stack. +*/ + +#define kBlitterStorageLongCount 40 + +class SkAutoBlitterChoose { +public: + SkAutoBlitterChoose(const SkBitmap& device, const SkMatrix& matrix, const SkPaint& paint) + { + fBlitter = SkBlitter::Choose(device, matrix, paint, fStorage, sizeof(fStorage)); + } + ~SkAutoBlitterChoose(); + + SkBlitter* operator->() { return fBlitter; } + SkBlitter* get() const { return fBlitter; } + +private: + SkBlitter* fBlitter; + uint32_t fStorage[kBlitterStorageLongCount]; +}; + +SkAutoBlitterChoose::~SkAutoBlitterChoose() +{ + if ((void*)fBlitter == (void*)fStorage) + fBlitter->~SkBlitter(); + else + SkDELETE(fBlitter); +} + +class SkAutoBitmapShaderInstall { +public: + SkAutoBitmapShaderInstall(const SkBitmap& src, const SkPaint* paint) : fPaint((SkPaint*)paint) + { + fPrevShader = paint->getShader(); + fPrevShader->safeRef(); + fPaint->setShader(SkShader::CreateBitmapShader( src, false, paint->getFilterType(), + SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, + fStorage, sizeof(fStorage))); + } + ~SkAutoBitmapShaderInstall() + { + SkShader* shader = fPaint->getShader(); + + fPaint->setShader(fPrevShader); + fPrevShader->safeUnref(); + + if ((void*)shader == (void*)fStorage) + shader->~SkShader(); + else + SkDELETE(shader); + } +private: + SkPaint* fPaint; + SkShader* fPrevShader; + uint32_t fStorage[kBlitterStorageLongCount]; +}; + +class SkAutoPaintStyleRestore { +public: + SkAutoPaintStyleRestore(const SkPaint& paint, SkPaint::Style style) + : fPaint((SkPaint&)paint) + { + fStyle = paint.getStyle(); // record the old + fPaint.setStyle(style); // change it to the specified style + } + ~SkAutoPaintStyleRestore() + { + fPaint.setStyle(fStyle); // restore the old + } +private: + SkPaint& fPaint; + SkPaint::Style fStyle; + + // illegal + SkAutoPaintStyleRestore(const SkAutoPaintStyleRestore&); + SkAutoPaintStyleRestore& operator=(const SkAutoPaintStyleRestore&); +}; + +/////////////////////////////////////////////////////////////////////////////// + +SkDraw::SkDraw(const SkCanvas& canvas) +{ + fDevice = &canvas.getCurrBitmap(); + fMatrix = &canvas.getTotalMatrix(); + fClip = &canvas.getTotalClip(); + fBounder = canvas.getBounder(); + fMapPtProc = canvas.getCurrMapPtProc(); + + SkDEBUGCODE(this->validate();) +} + +void SkDraw::drawPaint(const SkPaint& paint) +{ + SkDEBUGCODE(this->validate();) + + if (fClip->isEmpty()) + return; + + SkRect16 devRect; + devRect.set(0, 0, fDevice->width(), fDevice->height()); + + if (fBounder && !fBounder->doIRect(devRect, *fClip)) + return; + + SkAutoBlitterChoose blitter(*fDevice, *fMatrix, paint); + SkScan::FillDevRect(devRect, fClip, blitter.get()); +} + +void SkDraw::drawLine(const SkPoint& start, const SkPoint& stop, const SkPaint& paint) +{ + SkDEBUGCODE(this->validate();) + + if (fClip->isEmpty() || + (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) // nothing to draw + return; + + if (!paint.getPathEffect() && !paint.getMaskFilter() && + !paint.getRasterizer() && paint.getStrokeWidth() == 0) // hairline + { + SkPoint pts[2]; + fMapPtProc(*fMatrix, start.fX, start.fY, &pts[0]); + fMapPtProc(*fMatrix, stop.fX, stop.fY, &pts[1]); + + if (fBounder && !fBounder->doHairline(pts[0], pts[1], paint, *fClip)) + return; + + SkAutoBlitterChoose blitter(*fDevice, *fMatrix, paint); + + if (paint.isAntiAliasOn()) + SkScan::AntiHairLine(pts[0], pts[1], fClip, blitter.get()); + else + SkScan::HairLine(pts[0], pts[1], fClip, blitter.get()); + } + else + { + SkPath path; + // temporarily mark the paint as framing + SkAutoPaintStyleRestore restore(paint, SkPaint::kStroke_Style); + + path.moveTo(start.fX, start.fY); + path.lineTo(stop.fX, stop.fY); + this->drawPath(path, paint); + } +} + +void SkDraw::drawRect(const SkRect& rect, const SkPaint& paint) +{ + SkDEBUGCODE(this->validate();) + + if (fClip->isEmpty() || + (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) // nothing to draw + return; + + if (paint.getPathEffect() || paint.getMaskFilter() || paint.getRasterizer() || + !fMatrix->rectStaysRect() || (paint.getStyle() != SkPaint::kFill_Style && (paint.getStrokeWidth() / 2) > 0)) + { + SkPath tmp; + tmp.addRect(rect); + tmp.setFillType(SkPath::kWinding_FillType); + this->drawPath(tmp, paint); + return; + } + + SkRect devRect; + fMapPtProc(*fMatrix, rect.fLeft, rect.fTop, (SkPoint*)&devRect.fLeft); + fMapPtProc(*fMatrix, rect.fRight, rect.fBottom, (SkPoint*)&devRect.fRight); + devRect.sort(); + + if (fBounder && !fBounder->doRect(devRect, paint, *fClip)) + return; + + SkAutoBlitterChoose blitter(*fDevice, *fMatrix, paint); + + if (paint.getStyle() == SkPaint::kFill_Style) + SkScan::FillRect(devRect, fClip, blitter.get()); + else + { + if (paint.isAntiAliasOn()) + SkScan::AntiHairRect(devRect, fClip, blitter.get()); + else + SkScan::HairRect(devRect, fClip, blitter.get()); + } +} + +void SkDraw::drawDevMask(const SkMask& srcM, const SkPaint& paint) +{ + if (srcM.fBounds.isEmpty()) + return; + + SkMask dstM; + const SkMask* mask = &srcM; + + dstM.fImage = NULL; + SkAutoMaskImage ami(&dstM, false); + + if (paint.getMaskFilter() && + paint.getMaskFilter()->filterMask(&dstM, srcM, *fMatrix, NULL)) + { + mask = &dstM; + } + + if (fBounder && !fBounder->doIRect(mask->fBounds, *fClip)) + return; + + SkAutoBlitterChoose blitter(*fDevice, *fMatrix, paint); + + blitter->blitMaskRegion(*mask, *fClip); +} + +void SkDraw::drawPath(const SkPath& origSrcPath, const SkPaint& paint, const SkMatrix* prePathMatrix, bool srcPathIsMutable) +{ + SkDEBUGCODE(this->validate();) + + if (fClip->isEmpty() || + (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) // nothing to draw + return; + + SkPath* pathPtr = (SkPath*)&origSrcPath; + bool doFill = true; + SkPath tmpPath; + SkMatrix tmpMatrix; + const SkMatrix* matrix = fMatrix; + + if (prePathMatrix) + { + if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style || paint.getRasterizer()) + { + SkPath* result = pathPtr; + + if (!srcPathIsMutable) + { + result = &tmpPath; + srcPathIsMutable = true; + } + pathPtr->transform(*prePathMatrix, result); + pathPtr = result; + } + else + { + tmpMatrix.setConcat(*matrix, *prePathMatrix); + matrix = &tmpMatrix; + } + } + // at this point we're done with prePathMatrix + SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;) + + if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) + { + doFill = paint.getFillPath(*pathPtr, &tmpPath); + pathPtr = &tmpPath; + } + + if (paint.getRasterizer()) + { + SkMask mask; + if (paint.getRasterizer()->rasterize(*pathPtr, *matrix, &fClip->getBounds(), + paint.getMaskFilter(), &mask, + SkMask::kComputeBoundsAndRenderImage_CreateMode)) + { + this->drawDevMask(mask, paint); + SkMask::FreeImage(mask.fImage); + } + return; + } + + // avoid possibly allocating a new path in transform if we can + SkPath* devPathPtr = srcPathIsMutable ? pathPtr : &tmpPath; + + // transform the path into device space + if (!pathPtr->transform(*matrix, devPathPtr)) + return; + + SkAutoBlitterChoose blitter(*fDevice, *fMatrix, paint); + + // how does filterPath() know to fill or hairline the path??? <mrr> + if (paint.getMaskFilter() && + paint.getMaskFilter()->filterPath(*devPathPtr, *fMatrix, *fClip, fBounder, blitter.get())) + { + return; // filterPath() called the blitter, so we're done + } + + if (fBounder && !fBounder->doPath(*devPathPtr, paint, *fClip, doFill)) + return; + + if (doFill) + { + if (paint.isAntiAliasOn()) + SkScan::AntiFillPath(*devPathPtr, fClip, blitter.get()); + else + SkScan::FillPath(*devPathPtr, fClip, blitter.get()); + } + else // hairline + { + if (paint.isAntiAliasOn()) + SkScan::AntiHairPath(*devPathPtr, fClip, blitter.get()); + else + SkScan::HairPath(*devPathPtr, fClip, blitter.get()); + } +} + +static inline bool just_translate(const SkMatrix& m) +{ + return (m.getType() & ~SkMatrix::kTranslate_Mask) == 0; +} + +void SkDraw::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint& paint) +{ + SkDEBUGCODE(this->validate();) + + if (fClip->isEmpty() || + bitmap.width() == 0 || bitmap.height() == 0 || bitmap.getConfig() == SkBitmap::kNo_Config || + (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) // nothing to draw + return; + + SkAutoPaintStyleRestore restore(paint, SkPaint::kFill_Style); + + SkMatrix matrix = *fMatrix; + matrix.preTranslate(x, y); + + if (NULL == paint.getColorFilter() && just_translate(matrix)) + { + int ix = SkScalarRound(matrix.getTranslateX()); + int iy = SkScalarRound(matrix.getTranslateY()); + U32 storage[kBlitterStorageLongCount]; + SkBlitter* blitter = SkBlitter::ChooseSprite(*fDevice, paint, bitmap, ix, iy, storage, sizeof(storage)); + if (blitter) + { + SkAutoTPlacementDelete<SkBlitter> ad(blitter, storage); + + SkRect16 ir; + ir.set(ix, iy, ix + bitmap.width(), iy + bitmap.height()); + + if (fBounder && !fBounder->doIRect(ir, *fClip)) + return; + + SkRegion::Cliperator iter(*fClip, ir); + const SkRect16& cr = iter.rect(); + + for (; !iter.done(); iter.next()) + { + SkASSERT(!cr.isEmpty()); + #if 0 + LOGI("blitRect(%d %d %d %d) [%d %d %p]\n", cr.fLeft, cr.fTop, cr.width(), cr.height(), + bitmap.width(), bitmap.height(), bitmap.getPixels()); + #endif + blitter->blitRect(cr.fLeft, cr.fTop, cr.width(), cr.height()); + } + return; + } + } + + SkAutoBitmapShaderInstall install(bitmap, &paint); + + // save our state + const SkMatrix* saveMatrix = fMatrix; + SkMatrix::MapPtProc saveProc = fMapPtProc; + + // jam in the new temp state + fMatrix = &matrix; + fMapPtProc = matrix.getMapPtProc(); + + // call ourself with a rect + { + SkRect r; + r.set(0, 0, SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height())); + // is this ok if paint has a rasterizer? <reed> + this->drawRect(r, paint); + } + + // restore our state + fMapPtProc = saveProc; + fMatrix = saveMatrix; +} + +void SkDraw::drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint& paint) +{ + SkDEBUGCODE(this->validate();) + + SkRect16 bounds; + bounds.set(x, y, x + bitmap.width(), y + bitmap.height()); + + if (fClip->quickReject(bounds) || + bitmap.getConfig() == SkBitmap::kNo_Config || + (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) + { + return; // nothing to draw + } + + SkAutoPaintStyleRestore restore(paint, SkPaint::kFill_Style); + + if (NULL == paint.getColorFilter()) + { + uint32_t storage[kBlitterStorageLongCount]; + SkBlitter* blitter = SkBlitter::ChooseSprite(*fDevice, paint, bitmap, x, y, storage, sizeof(storage)); + + if (blitter) + { + SkAutoTPlacementDelete<SkBlitter> ad(blitter, storage); + + if (fBounder && !fBounder->doIRect(bounds, *fClip)) + return; + + SkRegion::Cliperator iter(*fClip, bounds); + const SkRect16& cr = iter.rect(); + + for (; !iter.done(); iter.next()) + { + SkASSERT(!cr.isEmpty()); + blitter->blitRect(cr.fLeft, cr.fTop, cr.width(), cr.height()); + } + return; + } + } + + SkAutoBitmapShaderInstall install(bitmap, &paint); + + // save our state + const SkMatrix* saveMatrix = fMatrix; + SkMatrix::MapPtProc saveProc = fMapPtProc; + SkMatrix matrix; + SkRect r; + + // get a scalar version of our rect + r.set(bounds); + + // tell the shader our offset + matrix.setTranslate(r.fLeft, r.fTop); + paint.getShader()->setLocalMatrix(matrix); + + // jam in the new temp state + matrix.reset(); + fMatrix = &matrix; + fMapPtProc = matrix.getMapPtProc(); + + // call ourself with a rect + { + // is this OK if paint has a rasterizer? <reed> + this->drawRect(r, paint); + } + + // restore our state + fMapPtProc = saveProc; + fMatrix = saveMatrix; +} + +/////////////////////////////////////////////////////////////////////////////////// + +#include "SkScalerContext.h" +#include "SkGlyphCache.h" +#include "SkUtils.h" + +static void measure_text(SkGlyphCache* cache, SkUnicodeWalkerProc textProc, const char text[], + size_t byteLength, SkVector* stopVector) +{ + SkFixed x = 0, y = 0; + const char* stop = text + byteLength; + + while (text < stop) + { + const SkGlyph& glyph = cache->getMetrics(textProc(&text)); + x += glyph.fAdvanceX; + y += glyph.fAdvanceY; + } + stopVector->set(SkFixedToScalar(x), SkFixedToScalar(y)); + + SkASSERT(text == stop); +} + +static void measure_layout(SkGlyphCache* cache, const SkTextLayout::Rec rec[], int count, + const SkMatrix& matrix, SkVector* stopVector) +{ + SkFixed x = 0, y = 0; + + for (int i = 0; i < count; i++) + { + // should pass glyphID to the cache, when we have that + const SkGlyph& glyph = cache->getMetrics(rec[i].charCode()); + SkVector adv; + adv.set(rec[i].fDeltaAdvance, 0); + matrix.mapVectors(&adv, 1); + x += glyph.fAdvanceX + SkScalarToFixed(adv.fX); + y += glyph.fAdvanceY + SkScalarToFixed(adv.fY); + } + stopVector->set(SkFixedToScalar(x), SkFixedToScalar(y)); +} + +void SkDraw::drawText_asPaths(SkUnicodeWalkerProc textProc, const char text[], size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) +{ + SkDEBUGCODE(this->validate();) + + SkTextToPathIter iter(textProc, text, byteLength, paint, true, true); + + SkMatrix matrix; + matrix.setScale(iter.getPathScale(), iter.getPathScale(), 0, 0); + matrix.postTranslate(x, y); + + const SkPath* iterPath; + SkScalar xpos, prevXPos = 0; + + while ((iterPath = iter.next(&xpos)) != NULL) + { + matrix.postTranslate(xpos - prevXPos, 0); + this->drawPath(*iterPath, iter.getPaint(), &matrix, false); + prevXPos = xpos; + } +} + +#define kStdStrikeThru_Offset (-SK_Scalar1 * 6 / 21) +#define kStdUnderline_Offset (SK_Scalar1 / 9) +#define kStdUnderline_Thickness (SK_Scalar1 / 18) + +static void draw_paint_rect(SkDraw* draw, const SkPaint& paint, const SkRect& r, SkScalar textSize) +{ + if (paint.getStyle() == SkPaint::kFill_Style) + draw->drawRect(r, paint); + else + { + SkPaint p(paint); + p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth())); + draw->drawRect(r, p); + } +} + +static void handle_aftertext(SkDraw* draw, const SkPaint& paint, SkScalar width, const SkPoint& start) +{ + U32 flags = paint.getFlags(); + + if (flags & (SkPaint::kUnderlineText_Mask | SkPaint::kStrikeThruText_Mask)) + { + SkScalar textSize = paint.getTextSize(); + SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness); + SkRect r; + + r.fLeft = start.fX; + r.fRight = start.fX + width; + + if (flags & SkPaint::kUnderlineText_Mask) + { + SkScalar offset = start.fY + SkScalarMul(textSize, kStdUnderline_Offset); + r.fTop = offset; + r.fBottom = offset + height; + draw_paint_rect(draw, paint, r, textSize); + } + if (flags & SkPaint::kStrikeThruText_Mask) + { + SkScalar offset = start.fY + SkScalarMul(textSize, kStdStrikeThru_Offset); + r.fTop = offset; + r.fBottom = offset + height; + draw_paint_rect(draw, paint, r, textSize); + } + } +} + +#if defined _WIN32 && _MSC_VER >= 1300 // disable warning : local variable used without having been initialized +#pragma warning ( push ) +#pragma warning ( disable : 4701 ) +#endif + +static inline void draw_one_glyph(const SkGlyph& glyph, int left, int top, SkBounder* bounder, + const SkRegion& clip, SkBlitter* blitter, SkGlyphCache* cache) +{ + SkMask mask; + + int right = left + glyph.fWidth; + int bottom = top + glyph.fHeight; + + mask.fBounds.set(left, top, right, bottom); + + if (bounder == NULL && clip.quickContains(left, top, right, bottom)) + { + uint8_t* aa = (uint8_t*)glyph.fImage; + if (aa == NULL) + aa = (uint8_t*)cache->findImage(glyph.fCharCode); + + if (aa) + { + mask.fRowBytes = glyph.fRowBytes; + mask.fFormat = glyph.fMaskFormat; + mask.fImage = aa; + blitter->blitMask(mask, mask.fBounds); + } + } + else + { + SkRegion::Cliperator clipper(clip, mask.fBounds); + if (!clipper.done()) + { + const SkRect16& cr = clipper.rect(); + const uint8_t* aa = (const uint8_t*)glyph.fImage; + if (NULL == aa) + aa = (const uint8_t*)cache->findImage(glyph.fCharCode); + + if (aa && (bounder == NULL || bounder->doIRect(cr, clip))) + { + mask.fRowBytes = glyph.fRowBytes; + mask.fFormat = glyph.fMaskFormat; + mask.fImage = (uint8_t*)aa; + do { + blitter->blitMask(mask, cr); + clipper.next(); + } while (!clipper.done()); + } + } + } +} + +void SkDraw::drawText(SkUnicodeWalkerProc textProc, const char text[], size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) +{ + SkASSERT(byteLength == 0 || text != NULL); + + SkDEBUGCODE(this->validate();) + + if (text == NULL || byteLength == 0 || + fClip->isEmpty() || + (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) // nothing to draw + return; + + SkASSERT(textProc); + + SkScalar underlineWidth = 0; + SkPoint underlineStart; + + if (paint.getFlags() & (SkPaint::kUnderlineText_Mask | SkPaint::kStrikeThruText_Mask)) + { + underlineWidth = paint.privateMeasureText(textProc, text, byteLength, NULL, NULL); + + SkScalar offsetX = 0; + if (paint.getTextAlign() == SkPaint::kCenter_Align) + offsetX = SkScalarHalf(underlineWidth); + else if (paint.getTextAlign() == SkPaint::kRight_Align) + offsetX = underlineWidth; + + underlineStart.set(x - offsetX, y); + } + + if (paint.isLinearTextOn() || + (fMatrix->getType() & SkMatrix::kPerspective_Mask)) + { + this->drawText_asPaths(textProc, text, byteLength, x, y, paint); + handle_aftertext(this, paint, underlineWidth, underlineStart); + return; + } + + SkAutoGlyphCache autoCache(paint, fMatrix); + SkGlyphCache* cache = autoCache.getCache(); + SkAutoBlitterChoose blitter(*fDevice, *fMatrix, paint); + SkTextLayout* layout = paint.getTextLayout(); + + // transform our starting point + { + SkPoint loc; + fMapPtProc(*fMatrix, x, y, &loc); + x = loc.fX; + y = loc.fY; + } + + if (paint.getTextAlign() != SkPaint::kLeft_Align) // need to measure first + { + SkVector stop; + + if (layout) + { + SkAutoSTMalloc<32, SkTextLayout::Rec> storage(byteLength); + SkTextLayout::Rec* rec = storage.get(); + + int count = layout->layout(paint, text, byteLength, textProc, rec); + measure_layout(cache, rec, count, *fMatrix, &stop); + } + else + measure_text(cache, textProc, text, byteLength, &stop); + + SkScalar stopX = stop.fX; + SkScalar stopY = stop.fY; + + if (paint.getTextAlign() == SkPaint::kCenter_Align) + { + stopX = SkScalarHalf(stopX); + stopY = SkScalarHalf(stopY); + } + x -= stopX; + y -= stopY; + } + + // add a half now so we can trunc rather than round in the loop + SkFixed fx = SkScalarToFixed(x) + SK_FixedHalf; + SkFixed fy = SkScalarToFixed(y) + SK_FixedHalf; + const char* stop = text + byteLength; + const SkRegion& clip = *fClip; + SkBounder* bounder = fBounder; + SkBlitter* blit = blitter.get(); + + if (layout) + { + SkAutoSTMalloc<32, SkTextLayout::Rec> storage(byteLength); + SkTextLayout::Rec* rec = storage.get(); + + int count = layout->layout(paint, text, byteLength, textProc, rec); + for (int i = 0; i < count; i++) + { + // should pass rec[i].glyphID when we have it + const SkGlyph& glyph = cache->getMetrics(rec[i].charCode()); + SkVector advance; + + advance.set(rec[i].fDeltaAdvance, 0); + fMatrix->mapVectors(&advance, 1); + SkFixed dx = SkScalarToFixed(advance.fX); + SkFixed dy = SkScalarToFixed(advance.fY); + + if (glyph.fWidth) { + draw_one_glyph( glyph, + SkFixedFloor(fx + (dx >> 1)) + glyph.fLeft, + SkFixedFloor(fy + (dy >> 1)) + glyph.fTop, + fBounder, *fClip, blit, cache); + } + fx += glyph.fAdvanceX + dx; + fy += glyph.fAdvanceY + dy; + } + } + else // no layout object + { + while (text < stop) + { + const SkGlyph& glyph = cache->getMetrics(textProc(&text)); + + if (glyph.fWidth) { + draw_one_glyph( glyph, + SkFixedFloor(fx) + glyph.fLeft, SkFixedFloor(fy) + glyph.fTop, + bounder, clip, blit, cache); + } + fx += glyph.fAdvanceX; + fy += glyph.fAdvanceY; + } + } + + if (underlineWidth) + { + autoCache.release(); // release this now to free up the RAM + handle_aftertext(this, paint, underlineWidth, underlineStart); + } +} + +typedef void (*AlignProc)(const SkPoint&, const SkGlyph&, SkPoint16*); + +static void leftAlignProc(const SkPoint& loc, const SkGlyph& glyph, SkPoint16* dst) +{ + dst->set(SkScalarRound(loc.fX) + glyph.fLeft, + SkScalarRound(loc.fY) + glyph.fTop); +} + +static void centerAlignProc(const SkPoint& loc, const SkGlyph& glyph, SkPoint16* dst) +{ + dst->set(SkFixedRound(SkScalarToFixed(loc.fX) - (glyph.fAdvanceX >> 1)) + glyph.fLeft, + SkFixedRound(SkScalarToFixed(loc.fY) - (glyph.fAdvanceY >> 1)) + glyph.fTop); +} + +static void rightAlignProc(const SkPoint& loc, const SkGlyph& glyph, SkPoint16* dst) +{ + dst->set(SkFixedRound(SkScalarToFixed(loc.fX) - glyph.fAdvanceX) + glyph.fLeft, + SkFixedRound(SkScalarToFixed(loc.fY) - glyph.fAdvanceY) + glyph.fTop); +} + +static AlignProc pick_align_proc(SkPaint::Align align) +{ + static const AlignProc gProcs[] = { leftAlignProc, centerAlignProc, rightAlignProc }; + + SkASSERT((unsigned)align < SK_ARRAY_COUNT(gProcs)); + + return gProcs[align]; +} + +void SkDraw::drawPosText(SkUnicodeWalkerProc textProc, const char text[], size_t byteLength, + const SkPoint pos[], const SkPaint& paint) +{ + SkASSERT(byteLength == 0 || text != NULL); + + SkDEBUGCODE(this->validate();) + + if (text == NULL || byteLength == 0 || + fClip->isEmpty() || + (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) // nothing to draw + return; + + SkASSERT(textProc); + + if (paint.isLinearTextOn() || + (fMatrix->getType() & SkMatrix::kPerspective_Mask)) + { +// this->drawText_asPaths(textProc, text, byteLength, x, y, paint); + return; + } + + SkAutoGlyphCache autoCache(paint, fMatrix); + SkGlyphCache* cache = autoCache.getCache(); + SkAutoBlitterChoose blitter(*fDevice, *fMatrix, paint); + + const char* stop = text + byteLength; + const SkRegion& clip = *fClip; + SkBounder* bounder = fBounder; + SkBlitter* blit = blitter.get(); + SkMatrix::MapPtProc mapPtProc = fMapPtProc; + const SkMatrix& matrix = *fMatrix; + AlignProc alignProc = pick_align_proc(paint.getTextAlign()); + + while (text < stop) + { + const SkGlyph& glyph = cache->getMetrics(textProc(&text)); + + if (glyph.fWidth) + { + SkPoint loc; + mapPtProc(matrix, pos->fX, pos->fY, &loc); + + SkPoint16 devLoc; + alignProc(loc, glyph, &devLoc); + + draw_one_glyph( glyph, devLoc.fX, devLoc.fY, + bounder, clip, blit, cache); + } + pos += 1; + } +} + +#if defined _WIN32 && _MSC_VER >= 1300 +#pragma warning ( pop ) +#endif + +//////////////////////////////////////////////////////////////////////////////////////////// + +#include "SkPathMeasure.h" + +static void morphpoints(SkPoint dst[], const SkPoint src[], int count, + SkPathMeasure& meas, SkScalar offset, SkScalar scale) +{ + for (int i = 0; i < count; i++) + { + SkPoint pos; + SkVector tangent; + + SkScalar sx = SkScalarMul(src[i].fX, scale) + offset; + SkScalar sy = SkScalarMul(src[i].fY, scale); + + meas.getPosTan(sx, &pos, &tangent); + + SkMatrix matrix; + SkPoint pt; + + pt.set(sx, sy); + matrix.setSinCos(tangent.fY, tangent.fX, 0, 0); + matrix.preTranslate(-sx, 0); + matrix.postTranslate(pos.fX, pos.fY); + matrix.mapPoints(&dst[i], &pt, 1); + } +} + +/* TODO + + Need differentially more subdivisions when the follow-path is curvy. Not sure how to + determine that, but we need it. I guess a cheap answer is let the caller tell us, + but that seems like a cop-out. Another answer is to get Rob Johnson to figure it out. +*/ +static void morphpath(SkPath* dst, const SkPath& src, SkPathMeasure& meas, + SkScalar offset, SkScalar scale) +{ + SkPath::Iter iter(src, false); + SkPoint srcP[4], dstP[3]; + SkPath::Verb verb; + + while ((verb = iter.next(srcP)) != SkPath::kDone_Verb) + { + switch (verb) { + case SkPath::kMove_Verb: + morphpoints(dstP, srcP, 1, meas, offset, scale); + dst->moveTo(dstP[0]); + break; + case SkPath::kLine_Verb: + srcP[2] = srcP[1]; + srcP[1].set(SkScalarAve(srcP[0].fX, srcP[2].fX), + SkScalarAve(srcP[0].fY, srcP[2].fY)); + // fall through to quad + case SkPath::kQuad_Verb: + morphpoints(dstP, &srcP[1], 2, meas, offset, scale); + dst->quadTo(dstP[0], dstP[1]); + break; + case SkPath::kCubic_Verb: + morphpoints(dstP, &srcP[1], 3, meas, offset, scale); + dst->cubicTo(dstP[0], dstP[1], dstP[2]); + break; + case SkPath::kClose_Verb: + dst->close(); + break; + default: + SkASSERT(!"unknown verb"); + break; + } + } +} + +void SkDraw::drawTextOnPath(SkUnicodeWalkerProc textProc, const char text[], size_t byteLength, + const SkPath& follow, SkScalar offset, const SkPaint& paint) +{ + SkASSERT(byteLength == 0 || text != NULL); + + if (text == NULL || byteLength == 0 || + fClip->getBounds().isEmpty() || + (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) // nothing to draw + return; + + SkTextToPathIter iter(textProc, text, byteLength, paint, true, true); + SkPathMeasure meas(follow, false); + + if (paint.getTextAlign() != SkPaint::kLeft_Align) // need to measure first + { + SkScalar pathLen = meas.getLength(); + if (paint.getTextAlign() == SkPaint::kCenter_Align) + pathLen = SkScalarHalf(pathLen); + offset += pathLen; + } + + const SkPath* iterPath; + SkScalar xpos; + while ((iterPath = iter.next(&xpos)) != NULL) + { + SkPath tmp; + morphpath(&tmp, *iterPath, meas, offset + xpos, iter.getPathScale()); + this->drawPath(tmp, iter.getPaint()); + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + +void SkDraw::validate() const +{ + SkASSERT(fDevice != NULL); + SkASSERT(fMatrix != NULL); + SkASSERT(fClip != NULL); + + const SkRect16& cr = fClip->getBounds(); + SkRect16 br; + + br.set(0, 0, fDevice->width(), fDevice->height()); + SkASSERT(br.contains(cr)); +} + +#endif + +////////////////////////////////////////////////////////////////////////////////////////// + +bool SkBounder::doIRect(const SkRect16& r, const SkRegion& clip) +{ + SkRect16 rr; + return rr.intersect(clip.getBounds(), r) && this->onIRect(rr); +} + +bool SkBounder::doHairline(const SkPoint& pt0, const SkPoint& pt1, const SkPaint& paint, const SkRegion& clip) +{ + SkRect16 r; + SkScalar v0, v1; + + v0 = pt0.fX; + v1 = pt1.fX; + if (v0 > v1) + SkTSwap<SkScalar>(v0, v1); + r.fLeft = SkToS16(SkScalarFloor(v0)); + r.fRight = SkToS16(SkScalarCeil(v1)); + + v0 = pt0.fY; + v1 = pt1.fY; + if (v0 > v1) + SkTSwap<SkScalar>(v0, v1); + r.fTop = SkToS16(SkScalarFloor(v0)); + r.fBottom = SkToS16(SkScalarCeil(v1)); + + if (paint.isAntiAliasOn()) + r.inset(-1, -1); + + return this->doIRect(r, clip); +} + +bool SkBounder::doRect(const SkRect& rect, const SkPaint& paint, const SkRegion& clip) +{ + SkRect16 r; + + if (paint.getStyle() == SkPaint::kFill_Style) + rect.round(&r); + else + { + int rad = -1; + rect.roundOut(&r); + if (paint.isAntiAliasOn()) + rad = -2; + r.inset(rad, rad); + } + return this->doIRect(r, clip); +} + +bool SkBounder::doPath(const SkPath& path, const SkPaint& paint, const SkRegion& clip, bool doFill) +{ + SkRect bounds; + SkRect16 r; + + path.computeBounds(&bounds, SkPath::kFast_BoundsType); + + if (doFill) + bounds.round(&r); + else // hairline + bounds.roundOut(&r); + + if (paint.isAntiAliasOn()) + r.inset(-1, -1); + + return this->doIRect(r, clip); +} + +void SkBounder::commit() +{ + // override in subclass +} + +//////////////////////////////////////////////////////////////////////////////////////////////// + +#include "SkPath.h" +#include "SkDraw.h" +#include "SkRegion.h" +#include "SkBlitter.h" + +static bool compute_bounds(const SkPath& devPath, const SkRect16* clipBounds, + SkMaskFilter* filter, const SkMatrix* filterMatrix, + SkRect16* bounds) +{ + if (devPath.isEmpty()) + return false; + + SkPoint16 margin; + margin.set(0, 0); + + // init our bounds from the path + { + SkRect pathBounds; + devPath.computeBounds(&pathBounds, SkPath::kExact_BoundsType); + pathBounds.inset(-SK_ScalarHalf, -SK_ScalarHalf); + pathBounds.roundOut(bounds); + } + + if (filter) + { + SkASSERT(filterMatrix); + + SkMask srcM, dstM; + + srcM.fBounds = *bounds; + srcM.fFormat = SkMask::kA8_Format; + srcM.fImage = NULL; + if (!filter->filterMask(&dstM, srcM, *filterMatrix, &margin)) + return false; + + *bounds = dstM.fBounds; + } + + if (clipBounds && !SkRect16::Intersects(*clipBounds, *bounds)) + return false; + + // (possibly) trim the srcM bounds to reflect the clip + // (plus whatever slop the filter needs) + if (clipBounds && !clipBounds->contains(*bounds)) + { + SkRect16 tmp = *bounds; + (void)tmp.intersect(*clipBounds); + tmp.inset(-margin.fX, -margin.fY); + (void)bounds->intersect(tmp); + } + + return true; +} + +static void draw_into_mask(const SkMask& mask, const SkPath& devPath) +{ + SkBitmap bm; + SkDraw draw; + SkRegion clipRgn; + SkMatrix matrix; + SkPaint paint; + + bm.setConfig(SkBitmap::kA8_Config, mask.fBounds.width(), mask.fBounds.height(), mask.fRowBytes); + bm.setPixels(mask.fImage); + + clipRgn.setRect(0, 0, mask.fBounds.width(), mask.fBounds.height()); + matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft), + -SkIntToScalar(mask.fBounds.fTop)); + + draw.fDevice = &bm; + draw.fClip = &clipRgn; + draw.fMatrix = &matrix; + draw.fMapPtProc = matrix.getMapPtProc(); + draw.fBounder = NULL; + paint.setAntiAliasOn(true); + draw.drawPath(devPath, paint); +} + +bool SkDraw::DrawToMask(const SkPath& devPath, const SkRect16* clipBounds, + SkMaskFilter* filter, const SkMatrix* filterMatrix, + SkMask* mask, SkMask::CreateMode mode) +{ + if (SkMask::kJustRenderImage_CreateMode != mode) + { + if (!compute_bounds(devPath, clipBounds, filter, filterMatrix, &mask->fBounds)) + return false; + } + + if (SkMask::kComputeBoundsAndRenderImage_CreateMode == mode) + { + mask->fFormat = SkMask::kA8_Format; + mask->fRowBytes = mask->fBounds.width(); + mask->fImage = SkMask::AllocImage(mask->computeImageSize()); + memset(mask->fImage, 0, mask->computeImageSize()); + } + + if (SkMask::kJustComputeBounds_CreateMode != mode) + draw_into_mask(*mask, devPath); + + return true; +} + |