LcdTextGM() { const int pointSize = 36; textHeight = SkIntToScalar(pointSize); }
virtual void onDraw(SkCanvas* canvas) { SkScalar x = SkIntToScalar(100); SkScalar y = SkIntToScalar(88); SkPaint paint; paint.setAntiAlias(true); paint.setTextSize(SkIntToScalar(100)); paint.setStrokeWidth(SkIntToScalar(5)); SkTypeface* face = SkTypeface::CreateFromName("Papyrus", SkTypeface::kNormal); SkSafeUnref(paint.setTypeface(face)); show_bold(canvas, "Hello", 5, x, y, paint); face = SkTypeface::CreateFromName("Hiragino Maru Gothic Pro", SkTypeface::kNormal); SkSafeUnref(paint.setTypeface(face)); const unsigned char hyphen[] = { 0xE3, 0x83, 0xBC }; show_bold(canvas, hyphen, SK_ARRAY_COUNT(hyphen), x + SkIntToScalar(300), y, paint); paint.setStyle(SkPaint::kStrokeAndFill_Style); SkPath path; path.setFillType(SkPath::kWinding_FillType); path.addCircle(x, y + SkIntToScalar(200), SkIntToScalar(50), SkPath::kCW_Direction); path.addCircle(x, y + SkIntToScalar(200), SkIntToScalar(40), SkPath::kCCW_Direction); canvas->drawPath(path, paint); SkPath path2; path2.setFillType(SkPath::kWinding_FillType); path2.addCircle(x + SkIntToScalar(120), y + SkIntToScalar(200), SkIntToScalar(50), SkPath::kCCW_Direction); path2.addCircle(x + SkIntToScalar(120), y + SkIntToScalar(200), SkIntToScalar(40), SkPath::kCW_Direction); canvas->drawPath(path2, paint); path2.reset(); path2.addCircle(x + SkIntToScalar(240), y + SkIntToScalar(200), SkIntToScalar(50), SkPath::kCCW_Direction); canvas->drawPath(path2, paint); SkASSERT(path2.cheapIsDirection(SkPath::kCCW_Direction)); path2.reset(); SkASSERT(!path2.cheapComputeDirection(NULL)); path2.addCircle(x + SkIntToScalar(360), y + SkIntToScalar(200), SkIntToScalar(50), SkPath::kCW_Direction); SkASSERT(path2.cheapIsDirection(SkPath::kCW_Direction)); canvas->drawPath(path2, paint); }
//////////////////////////////////////////////////////////////////////////////// // Create a 8-bit clip mask in alpha bool GrClipMaskManager::createAlphaClipMask(const GrClipData& clipDataIn, GrTexture** result, GrIRect *devResultBounds) { GrAssert(NULL != devResultBounds); GrAssert(kNone_ClipMaskType == fCurrClipMaskType); if (this->clipMaskPreamble(clipDataIn, result, devResultBounds)) { fCurrClipMaskType = kAlpha_ClipMaskType; return true; } // Note: 'resultBounds' is in device (as opposed to canvas) coordinates GrTexture* accum = fAACache.getLastMask(); if (NULL == accum) { fAACache.reset(); return false; } GrDrawTarget::AutoStateRestore asr(fGpu, GrDrawTarget::kReset_ASRInit); GrDrawState* drawState = fGpu->drawState(); GrDrawTarget::AutoGeometryPush agp(fGpu); if (0 != devResultBounds->fTop || 0 != devResultBounds->fLeft || 0 != clipDataIn.fOrigin.fX || 0 != clipDataIn.fOrigin.fY) { // if we were able to trim down the size of the mask we need to // offset the paths & rects that will be used to compute it drawState->viewMatrix()->setTranslate( SkIntToScalar(-devResultBounds->fLeft-clipDataIn.fOrigin.fX), SkIntToScalar(-devResultBounds->fTop-clipDataIn.fOrigin.fY)); } bool clearToInside; SkRegion::Op firstOp = SkRegion::kReplace_Op; // suppress warning SkClipStack::Iter iter(*clipDataIn.fClipStack, SkClipStack::Iter::kBottom_IterStart); const SkClipStack::Iter::Clip* clip = process_initial_clip_elements(&iter, *devResultBounds, &clearToInside, &firstOp, clipDataIn); fGpu->clear(NULL, clearToInside ? 0xffffffff : 0x00000000, accum->asRenderTarget()); GrAutoScratchTexture temp; bool first = true; // walk through each clip element and perform its set op for ( ; NULL != clip; clip = iter.next()) { SkRegion::Op op = clip->fOp; if (first) { first = false; op = firstOp; } if (SkRegion::kReplace_Op == op) { // clear the accumulator and draw the new object directly into it fGpu->clear(NULL, 0x00000000, accum->asRenderTarget()); setup_boolean_blendcoeffs(drawState, op); this->drawClipShape(accum, clip, *devResultBounds); } else if (SkRegion::kReverseDifference_Op == op || SkRegion::kIntersect_Op == op) { // there is no point in intersecting a screen filling rectangle. if (SkRegion::kIntersect_Op == op && NULL != clip->fRect && contains(*clip->fRect, *devResultBounds, clipDataIn.fOrigin)) { continue; } getTemp(*devResultBounds, &temp); if (NULL == temp.texture()) { fAACache.reset(); return false; } // clear the temp target & draw into it fGpu->clear(NULL, 0x00000000, temp.texture()->asRenderTarget()); setup_boolean_blendcoeffs(drawState, SkRegion::kReplace_Op); this->drawClipShape(temp.texture(), clip, *devResultBounds); // TODO: rather than adding these two translations here // compute the bounding box needed to render the texture // into temp if (0 != devResultBounds->fTop || 0 != devResultBounds->fLeft || 0 != clipDataIn.fOrigin.fX || 0 != clipDataIn.fOrigin.fY) { // In order for the merge of the temp clip into the accumulator // to work we need to disable the translation drawState->viewMatrix()->reset(); } // Now draw into the accumulator using the real operation // and the temp buffer as a texture setup_boolean_blendcoeffs(drawState, op); this->drawTexture(accum, temp.texture()); if (0 != devResultBounds->fTop || 0 != devResultBounds->fLeft || 0 != clipDataIn.fOrigin.fX || 0 != clipDataIn.fOrigin.fY) { drawState->viewMatrix()->setTranslate( SkIntToScalar(-devResultBounds->fLeft-clipDataIn.fOrigin.fX), SkIntToScalar(-devResultBounds->fTop-clipDataIn.fOrigin.fY)); } } else { // all the remaining ops can just be directly draw into // the accumulation buffer setup_boolean_blendcoeffs(drawState, op); this->drawClipShape(accum, clip, *devResultBounds); } } *result = accum; fCurrClipMaskType = kAlpha_ClipMaskType; return true; }
static SkRect make_rect(int l, int t, int r, int b) { SkRect rect; rect.set(SkIntToScalar(l), SkIntToScalar(t), SkIntToScalar(r), SkIntToScalar(b)); return rect; }
void onOnceBeforeDraw() override { this->setBGColor(0xFFDDDDDD); fCirclePath.addCircle(SkIntToScalar(20), SkIntToScalar(20), SkIntToScalar(10) ); fRect.set(SkIntToScalar(10), SkIntToScalar(10), SkIntToScalar(30), SkIntToScalar(30)); }
static SkRect make_rect() { return SkRect::MakeWH(SkIntToScalar(R(static_cast<float>(kBitmapSize))), SkIntToScalar(R(static_cast<float>(kBitmapSize)))); }
{ SkXfermode::kSrc_Mode, "Src" }, { SkXfermode::kDst_Mode, "Dst" }, { SkXfermode::kSrcOver_Mode, "SrcOver" }, { SkXfermode::kDstOver_Mode, "DstOver" }, { SkXfermode::kSrcIn_Mode, "SrcIn" }, { SkXfermode::kDstIn_Mode, "DstIn" }, { SkXfermode::kSrcOut_Mode, "SrcOut" }, { SkXfermode::kDstOut_Mode, "DstOut" }, { SkXfermode::kSrcATop_Mode, "SrcATop" }, { SkXfermode::kDstATop_Mode, "DstATop" }, { SkXfermode::kXor_Mode, "Xor" }, }; const int gWidth = 64; const int gHeight = 64; const SkScalar W = SkIntToScalar(gWidth); const SkScalar H = SkIntToScalar(gHeight); static SkScalar drawCell(SkCanvas* canvas, SkXfermode* mode, SkAlpha a0, SkAlpha a1) { SkPaint paint; paint.setAntiAlias(true); SkRect r = SkRect::MakeWH(W, H); r.inset(W/10, H/10); paint.setColor(SK_ColorBLUE); paint.setAlpha(a0); canvas->drawOval(r, paint);
} void setAssertMessageFormat(const char* format) { fAssertMessageFormat = format; } private: SkString fAssertMessage; const char* fAssertMessageFormat; }; /////////////////////////////////////////////////////////////////////////////// // Constants used by test steps const SkRect kTestRect = SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(2), SkIntToScalar(1)); static SkMatrix testMatrix() { SkMatrix matrix; matrix.reset(); matrix.setScale(SkIntToScalar(2), SkIntToScalar(3)); return matrix; } const SkMatrix kTestMatrix = testMatrix(); static SkPath testPath() { SkPath path; path.addRect(SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(2), SkIntToScalar(1))); return path; } const SkPath kTestPath = testPath();
static SkMatrix testMatrix() { SkMatrix matrix; matrix.reset(); matrix.setScale(SkIntToScalar(2), SkIntToScalar(3)); return matrix; }
bool SkDisplacementMapEffect::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx, SkBitmap* result, SkIPoint* offset) const { SkBitmap colorBM = src; SkIPoint colorOffset = SkIPoint::Make(0, 0); if (getColorInput() && !getColorInput()->getInputResultGPU(proxy, src, ctx, &colorBM, &colorOffset)) { return false; } SkBitmap displacementBM = src; SkIPoint displacementOffset = SkIPoint::Make(0, 0); if (getDisplacementInput() && !getDisplacementInput()->getInputResultGPU(proxy, src, ctx, &displacementBM, &displacementOffset)) { return false; } SkIRect bounds; // Since GrDisplacementMapEffect does bounds checking on color pixel access, we don't need to // pad the color bitmap to bounds here. if (!this->applyCropRect(ctx, colorBM, colorOffset, &bounds)) { return false; } SkIRect displBounds; if (!this->applyCropRect(ctx, proxy, displacementBM, &displacementOffset, &displBounds, &displacementBM)) { return false; } if (!bounds.intersect(displBounds)) { return false; } GrTexture* color = colorBM.getTexture(); GrTexture* displacement = displacementBM.getTexture(); GrContext* context = color->getContext(); GrSurfaceDesc desc; desc.fFlags = kRenderTarget_GrSurfaceFlag; desc.fWidth = bounds.width(); desc.fHeight = bounds.height(); desc.fConfig = kSkia8888_GrPixelConfig; SkAutoTUnref<GrTexture> dst(context->textureProvider()->refScratchTexture(desc, GrTextureProvider::kApprox_ScratchTexMatch)); if (!dst) { return false; } SkVector scale = SkVector::Make(fScale, fScale); ctx.ctm().mapVectors(&scale, 1); GrPaint paint; SkMatrix offsetMatrix = GrCoordTransform::MakeDivByTextureWHMatrix(displacement); offsetMatrix.preTranslate(SkIntToScalar(colorOffset.fX - displacementOffset.fX), SkIntToScalar(colorOffset.fY - displacementOffset.fY)); paint.addColorProcessor( GrDisplacementMapEffect::Create(fXChannelSelector, fYChannelSelector, scale, displacement, offsetMatrix, color, colorBM.dimensions()))->unref(); SkIRect colorBounds = bounds; colorBounds.offset(-colorOffset); SkMatrix matrix; matrix.setTranslate(-SkIntToScalar(colorBounds.x()), -SkIntToScalar(colorBounds.y())); context->drawRect(dst->asRenderTarget(), GrClip::WideOpen(), paint, matrix, SkRect::Make(colorBounds)); offset->fX = bounds.left(); offset->fY = bounds.top(); WrapTexture(dst, bounds.width(), bounds.height(), result); return true; }
virtual void onDrawContent(SkCanvas* canvas) { #ifdef SK_DEBUG if (true) { SkRegion a, b, c; test_union_bug_1505668(&a, &b, &c); if (false) // draw the result of the test { SkPaint paint; canvas->translate(SkIntToScalar(10), SkIntToScalar(10)); paint.setColor(SK_ColorRED); paint_rgn(canvas, a, paint); paint.setColor(0x800000FF); paint_rgn(canvas, b, paint); paint.setColor(SK_ColorBLACK); paint.setStyle(SkPaint::kStroke_Style); // paint_rgn(canvas, c, paint); return; } } #endif static const struct { SkColor fColor; const char* fName; SkRegion::Op fOp; } gOps[] = { { SK_ColorBLACK, "Difference", SkRegion::kDifference_Op }, { SK_ColorRED, "Intersect", SkRegion::kIntersect_Op }, { 0xFF008800, "Union", SkRegion::kUnion_Op }, { SK_ColorBLUE, "XOR", SkRegion::kXOR_Op } }; SkPaint textPaint; textPaint.setAntiAlias(true); textPaint.setTextSize(SK_Scalar1*24); this->drawOrig(canvas, false); canvas->save(); canvas->translate(SkIntToScalar(200), 0); this->drawRgnOped(canvas, SkRegion::kUnion_Op, SK_ColorBLACK); canvas->restore(); canvas->translate(0, SkIntToScalar(200)); for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); op++) { canvas->drawText(gOps[op].fName, strlen(gOps[op].fName), SkIntToScalar(75), SkIntToScalar(50), textPaint); this->drawRgnOped(canvas, gOps[op].fOp, gOps[op].fColor); canvas->save(); canvas->translate(0, SkIntToScalar(200)); this->drawPathOped(canvas, gOps[op].fOp, gOps[op].fColor); canvas->restore(); canvas->translate(SkIntToScalar(200), 0); } }
void GrStencilAndCoverTextContext::init(GrRenderTarget* rt, const GrClip& clip, const GrPaint& paint, const SkPaint& skPaint, size_t textByteLength, RenderMode renderMode, const SkMatrix& viewMatrix, const SkIRect& regionClipBounds) { fClip = clip; fRenderTarget.reset(SkRef(rt)); fRegionClipBounds = regionClipBounds; fClip.getConservativeBounds(fRenderTarget->width(), fRenderTarget->height(), &fClipRect); fPaint = paint; fSkPaint = skPaint; fContextInitialMatrix = viewMatrix; fViewMatrix = viewMatrix; fLocalMatrix = SkMatrix::I(); const bool otherBackendsWillDrawAsPaths = SkDraw::ShouldDrawTextAsPaths(skPaint, fContextInitialMatrix); fUsingDeviceSpaceGlyphs = !otherBackendsWillDrawAsPaths && kMaxAccuracy_RenderMode == renderMode && SkToBool(fContextInitialMatrix.getType() & (SkMatrix::kScale_Mask | SkMatrix::kAffine_Mask)); if (fUsingDeviceSpaceGlyphs) { // SkDraw::ShouldDrawTextAsPaths takes care of perspective transforms. SkASSERT(!fContextInitialMatrix.hasPerspective()); // The whole shape (including stroke) will be baked into the glyph outlines. Make // NVPR just fill the baked shapes. fStroke = GrStrokeInfo(SkStrokeRec::kFill_InitStyle); fTextRatio = fTextInverseRatio = 1.0f; // Glyphs loaded by GPU path rendering have an inverted y-direction. SkMatrix m; m.setScale(1, -1); fViewMatrix = m; // Post-flip the initial matrix so we're left with just the flip after // the paint preConcats the inverse. m = fContextInitialMatrix; m.postScale(1, -1); if (!m.invert(&fLocalMatrix)) { SkDebugf("Not invertible!\n"); return; } fGlyphCache = fSkPaint.detachCache(&fSurfaceProps, &fContextInitialMatrix, true /*ignoreGamma*/); fGlyphs = get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(), &fGlyphCache->getDescriptor(), fStroke); } else { // Don't bake strokes into the glyph outlines. We will stroke the glyphs // using the GPU instead. This is the fast path. fStroke = GrStrokeInfo(fSkPaint); fSkPaint.setStyle(SkPaint::kFill_Style); if (fStroke.isHairlineStyle()) { // Approximate hairline stroke. SkScalar strokeWidth = SK_Scalar1 / (SkVector::Make(fContextInitialMatrix.getScaleX(), fContextInitialMatrix.getSkewY()).length()); fStroke.setStrokeStyle(strokeWidth, false /*strokeAndFill*/); } else if (fSkPaint.isFakeBoldText() && #ifdef SK_USE_FREETYPE_EMBOLDEN kMaxPerformance_RenderMode == renderMode && #endif SkStrokeRec::kStroke_Style != fStroke.getStyle()) { // Instead of baking fake bold into the glyph outlines, do it with the GPU stroke. SkScalar fakeBoldScale = SkScalarInterpFunc(fSkPaint.getTextSize(), kStdFakeBoldInterpKeys, kStdFakeBoldInterpValues, kStdFakeBoldInterpLength); SkScalar extra = SkScalarMul(fSkPaint.getTextSize(), fakeBoldScale); fStroke.setStrokeStyle(fStroke.needToApply() ? fStroke.getWidth() + extra : extra, true /*strokeAndFill*/); fSkPaint.setFakeBoldText(false); } bool canUseRawPaths; if (!fStroke.isDashed() && (otherBackendsWillDrawAsPaths || kMaxPerformance_RenderMode == renderMode)) { // We can draw the glyphs from canonically sized paths. fTextRatio = fSkPaint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths; fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fSkPaint.getTextSize(); // Compensate for the glyphs being scaled by fTextRatio. if (!fStroke.isFillStyle()) { fStroke.setStrokeStyle(fStroke.getWidth() / fTextRatio, SkStrokeRec::kStrokeAndFill_Style == fStroke.getStyle()); } fSkPaint.setLinearText(true); fSkPaint.setLCDRenderText(false); fSkPaint.setAutohinted(false); fSkPaint.setHinting(SkPaint::kNo_Hinting); fSkPaint.setSubpixelText(true); fSkPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths)); canUseRawPaths = SK_Scalar1 == fSkPaint.getTextScaleX() && 0 == fSkPaint.getTextSkewX() && !fSkPaint.isFakeBoldText() && !fSkPaint.isVerticalText(); } else { fTextRatio = fTextInverseRatio = 1.0f; canUseRawPaths = false; } SkMatrix textMatrix; // Glyphs loaded by GPU path rendering have an inverted y-direction. textMatrix.setScale(fTextRatio, -fTextRatio); fViewMatrix.preConcat(textMatrix); fLocalMatrix = textMatrix; fGlyphCache = fSkPaint.detachCache(&fSurfaceProps, nullptr, true /*ignoreGamma*/); fGlyphs = canUseRawPaths ? get_gr_glyphs(fContext, fSkPaint.getTypeface(), nullptr, fStroke) : get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(), &fGlyphCache->getDescriptor(), fStroke); } }
virtual void begin(const SkIRect& uvBounds, SkPath* ) { fUVBounds.set(SkIntToScalar(uvBounds.fLeft), SkIntToScalar(uvBounds.fTop), SkIntToScalar(uvBounds.fRight), SkIntToScalar(uvBounds.fBottom)); }
void NinePatch_Draw(SkCanvas* canvas, const SkRect& bounds, const SkBitmap& bitmap, const android::Res_png_9patch& chunk, const SkPaint* paint, SkRegion** outRegion) { if (canvas && canvas->quickReject(bounds)) { return; } SkPaint defaultPaint; if (NULL == paint) { // matches default dither in NinePatchDrawable.java. defaultPaint.setDither(true); paint = &defaultPaint; } const int32_t* xDivs = chunk.getXDivs(); const int32_t* yDivs = chunk.getYDivs(); // if our SkCanvas were back by GL we should enable this and draw this as // a mesh, which will be faster in most cases. if ((false)) { SkNinePatch::DrawMesh(canvas, bounds, bitmap, xDivs, chunk.numXDivs, yDivs, chunk.numYDivs, paint); return; } if (kUseTrace) { gTrace = true; } SkASSERT(canvas || outRegion); if (kUseTrace) { if (canvas) { const SkMatrix& m = canvas->getTotalMatrix(); ALOGV("ninepatch [%g %g %g] [%g %g %g]\n", SkScalarToFloat(m[0]), SkScalarToFloat(m[1]), SkScalarToFloat(m[2]), SkScalarToFloat(m[3]), SkScalarToFloat(m[4]), SkScalarToFloat(m[5])); } ALOGV("======== ninepatch bounds [%g %g]\n", SkScalarToFloat(bounds.width()), SkScalarToFloat(bounds.height())); ALOGV("======== ninepatch paint bm [%d,%d]\n", bitmap.width(), bitmap.height()); ALOGV("======== ninepatch xDivs [%d,%d]\n", xDivs[0], xDivs[1]); ALOGV("======== ninepatch yDivs [%d,%d]\n", yDivs[0], yDivs[1]); } if (bounds.isEmpty() || bitmap.width() == 0 || bitmap.height() == 0 || (paint && paint->getXfermode() == NULL && paint->getAlpha() == 0)) { if (kUseTrace) { ALOGV("======== abort ninepatch draw\n"); } return; } // should try a quick-reject test before calling lockPixels SkAutoLockPixels alp(bitmap); // after the lock, it is valid to check getPixels() if (bitmap.getPixels() == NULL) return; const bool hasXfer = paint->getXfermode() != NULL; SkRect dst; SkIRect src; const int32_t x0 = xDivs[0]; const int32_t y0 = yDivs[0]; const SkColor initColor = ((SkPaint*)paint)->getColor(); const uint8_t numXDivs = chunk.numXDivs; const uint8_t numYDivs = chunk.numYDivs; int i; int j; int colorIndex = 0; uint32_t color; bool xIsStretchable; const bool initialXIsStretchable = (x0 == 0); bool yIsStretchable = (y0 == 0); const int bitmapWidth = bitmap.width(); const int bitmapHeight = bitmap.height(); // Number of bytes needed for dstRights array. // Need to cast numXDivs to a larger type to avoid overflow. const size_t dstBytes = ((size_t) numXDivs + 1) * sizeof(SkScalar); SkScalar* dstRights = (SkScalar*) alloca(dstBytes); bool dstRightsHaveBeenCached = false; int numStretchyXPixelsRemaining = 0; for (i = 0; i < numXDivs; i += 2) { numStretchyXPixelsRemaining += xDivs[i + 1] - xDivs[i]; } int numFixedXPixelsRemaining = bitmapWidth - numStretchyXPixelsRemaining; int numStretchyYPixelsRemaining = 0; for (i = 0; i < numYDivs; i += 2) { numStretchyYPixelsRemaining += yDivs[i + 1] - yDivs[i]; } int numFixedYPixelsRemaining = bitmapHeight - numStretchyYPixelsRemaining; if (kUseTrace) { ALOGV("NinePatch [%d %d] bounds [%g %g %g %g] divs [%d %d]\n", bitmap.width(), bitmap.height(), SkScalarToFloat(bounds.fLeft), SkScalarToFloat(bounds.fTop), SkScalarToFloat(bounds.width()), SkScalarToFloat(bounds.height()), numXDivs, numYDivs); } src.fTop = 0; dst.fTop = bounds.fTop; // The first row always starts with the top being at y=0 and the bottom // being either yDivs[1] (if yDivs[0]=0) or yDivs[0]. In the former case // the first row is stretchable along the Y axis, otherwise it is fixed. // The last row always ends with the bottom being bitmap.height and the top // being either yDivs[numYDivs-2] (if yDivs[numYDivs-1]=bitmap.height) or // yDivs[numYDivs-1]. In the former case the last row is stretchable along // the Y axis, otherwise it is fixed. // // The first and last columns are similarly treated with respect to the X // axis. // // The above is to help explain some of the special casing that goes on the // code below. // The initial yDiv and whether the first row is considered stretchable or // not depends on whether yDiv[0] was zero or not. for (j = yIsStretchable ? 1 : 0; j <= numYDivs && src.fTop < bitmapHeight; j++, yIsStretchable = !yIsStretchable) { src.fLeft = 0; dst.fLeft = bounds.fLeft; if (j == numYDivs) { src.fBottom = bitmapHeight; dst.fBottom = bounds.fBottom; } else { src.fBottom = yDivs[j]; const int srcYSize = src.fBottom - src.fTop; if (yIsStretchable) { dst.fBottom = dst.fTop + calculateStretch(bounds.fBottom, dst.fTop, srcYSize, numStretchyYPixelsRemaining, numFixedYPixelsRemaining); numStretchyYPixelsRemaining -= srcYSize; } else { dst.fBottom = dst.fTop + SkIntToScalar(srcYSize); numFixedYPixelsRemaining -= srcYSize; } } xIsStretchable = initialXIsStretchable; // The initial xDiv and whether the first column is considered // stretchable or not depends on whether xDiv[0] was zero or not. const uint32_t* colors = chunk.getColors(); for (i = xIsStretchable ? 1 : 0; i <= numXDivs && src.fLeft < bitmapWidth; i++, xIsStretchable = !xIsStretchable) { color = colors[colorIndex++]; if (i == numXDivs) { src.fRight = bitmapWidth; dst.fRight = bounds.fRight; } else { src.fRight = xDivs[i]; if (dstRightsHaveBeenCached) { dst.fRight = dstRights[i]; } else { const int srcXSize = src.fRight - src.fLeft; if (xIsStretchable) { dst.fRight = dst.fLeft + calculateStretch(bounds.fRight, dst.fLeft, srcXSize, numStretchyXPixelsRemaining, numFixedXPixelsRemaining); numStretchyXPixelsRemaining -= srcXSize; } else { dst.fRight = dst.fLeft + SkIntToScalar(srcXSize); numFixedXPixelsRemaining -= srcXSize; } dstRights[i] = dst.fRight; } } // If this horizontal patch is too small to be displayed, leave // the destination left edge where it is and go on to the next patch // in the source. if (src.fLeft >= src.fRight) { src.fLeft = src.fRight; continue; } // Make sure that we actually have room to draw any bits if (dst.fRight <= dst.fLeft || dst.fBottom <= dst.fTop) { goto nextDiv; } // If this patch is transparent, skip and don't draw. if (color == android::Res_png_9patch::TRANSPARENT_COLOR && !hasXfer) { if (outRegion) { if (*outRegion == NULL) { *outRegion = new SkRegion(); } SkIRect idst; dst.round(&idst); //ALOGI("Adding trans rect: (%d,%d)-(%d,%d)\n", // idst.fLeft, idst.fTop, idst.fRight, idst.fBottom); (*outRegion)->op(idst, SkRegion::kUnion_Op); } goto nextDiv; } if (canvas) { if (kUseTrace) { ALOGV("-- src [%d %d %d %d] dst [%g %g %g %g]\n", src.fLeft, src.fTop, src.width(), src.height(), SkScalarToFloat(dst.fLeft), SkScalarToFloat(dst.fTop), SkScalarToFloat(dst.width()), SkScalarToFloat(dst.height())); if (2 == src.width() && SkIntToScalar(5) == dst.width()) { ALOGV("--- skip patch\n"); } } drawStretchyPatch(canvas, src, dst, bitmap, *paint, initColor, color, hasXfer); } nextDiv: src.fLeft = src.fRight; dst.fLeft = dst.fRight; } src.fTop = src.fBottom; dst.fTop = dst.fBottom; dstRightsHaveBeenCached = true; } }
void onOnceBeforeDraw() override { this->setBGColor(sk_tool_utils::color_to_565(0xFFDDDDDD)); fCirclePath.addCircle(SkIntToScalar(20), SkIntToScalar(20), SkIntToScalar(10) ); fRect.set(SkIntToScalar(10), SkIntToScalar(10), SkIntToScalar(30), SkIntToScalar(30)); }
static SkPath testPath() { SkPath path; path.addRect(SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(2), SkIntToScalar(1))); return path; }
// Only called once. Could be part of the constructor. void init(SkScalar seed) { static const SkScalar gInvBlockSizef = SkScalarInvert(SkIntToScalar(kBlockSize)); // According to the SVG spec, we must truncate (not round) the seed value. fSeed = SkScalarTruncToInt(seed); // The seed value clamp to the range [1, kRandMaximum - 1]. if (fSeed <= 0) { fSeed = -(fSeed % (kRandMaximum - 1)) + 1; } if (fSeed > kRandMaximum - 1) { fSeed = kRandMaximum - 1; } for (int channel = 0; channel < 4; ++channel) { for (int i = 0; i < kBlockSize; ++i) { fLatticeSelector[i] = i; fNoise[channel][i][0] = (random() % (2 * kBlockSize)); fNoise[channel][i][1] = (random() % (2 * kBlockSize)); } } for (int i = kBlockSize - 1; i > 0; --i) { int k = fLatticeSelector[i]; int j = random() % kBlockSize; SkASSERT(j >= 0); SkASSERT(j < kBlockSize); fLatticeSelector[i] = fLatticeSelector[j]; fLatticeSelector[j] = k; } // Perform the permutations now { // Copy noise data uint16_t noise[4][kBlockSize][2]; for (int i = 0; i < kBlockSize; ++i) { for (int channel = 0; channel < 4; ++channel) { for (int j = 0; j < 2; ++j) { noise[channel][i][j] = fNoise[channel][i][j]; } } } // Do permutations on noise data for (int i = 0; i < kBlockSize; ++i) { for (int channel = 0; channel < 4; ++channel) { for (int j = 0; j < 2; ++j) { fNoise[channel][i][j] = noise[channel][fLatticeSelector[i]][j]; } } } } // Half of the largest possible value for 16 bit unsigned int static const SkScalar gHalfMax16bits = 32767.5f; // Compute gradients from permutated noise data for (int channel = 0; channel < 4; ++channel) { for (int i = 0; i < kBlockSize; ++i) { fGradient[channel][i] = SkPoint::Make( SkScalarMul(SkIntToScalar(fNoise[channel][i][0] - kBlockSize), gInvBlockSizef), SkScalarMul(SkIntToScalar(fNoise[channel][i][1] - kBlockSize), gInvBlockSizef)); fGradient[channel][i].normalize(); // Put the normalized gradient back into the noise data fNoise[channel][i][0] = SkScalarRoundToInt(SkScalarMul( fGradient[channel][i].fX + SK_Scalar1, gHalfMax16bits)); fNoise[channel][i][1] = SkScalarRoundToInt(SkScalarMul( fGradient[channel][i].fY + SK_Scalar1, gHalfMax16bits)); } } }
static void testOpLoopsMain(PathOpsThreadState* data) { #if DEBUG_SHOW_TEST_NAME strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH); #endif SkASSERT(data); PathOpsThreadState& state = *data; SkString pathStr; for (int a = 0 ; a < 6; ++a) { for (int b = a + 1 ; b < 7; ++b) { for (int c = 0 ; c < 6; ++c) { for (int d = c + 1 ; d < 7; ++d) { // define 4 points that form two lines that often cross; one line is (a, b) (c, d) SkVector v = {SkIntToScalar(a - c), SkIntToScalar(b - d)}; SkPoint midA = { SkIntToScalar(a * state.fA + c * (6 - state.fA)) / 6, SkIntToScalar(b * state.fA + d * (6 - state.fA)) / 6 }; SkPoint midB = { SkIntToScalar(a * state.fB + c * (6 - state.fB)) / 6, SkIntToScalar(b * state.fB + d * (6 - state.fB)) / 6 }; SkPoint endC = { midA.fX + v.fY * state.fC / 3, midA.fY + v.fX * state.fC / 3 }; SkPoint endD = { midB.fX - v.fY * state.fD / 3, midB.fY + v.fX * state.fD / 3 }; SkPath pathA, pathB; pathA.moveTo(SkIntToScalar(a), SkIntToScalar(b)); pathA.cubicTo(SkIntToScalar(c), SkIntToScalar(d), endC.fX, endC.fY, endD.fX, endD.fY); pathA.close(); pathB.moveTo(SkIntToScalar(c), SkIntToScalar(d)); pathB.cubicTo(endC.fX, endC.fY, endD.fX, endD.fY, SkIntToScalar(a), SkIntToScalar(b)); pathB.close(); // SkDebugf("%s\n", pathStr); if (state.fReporter->verbose()) { pathStr.printf("static void loop%d(skiatest::Reporter* reporter," " const char* filename) {\n", loopNo); pathStr.appendf(" SkPath path, pathB;\n"); pathStr.appendf(" path.moveTo(%d,%d);\n", a, b); pathStr.appendf(" path.cubicTo(%d,%d, ", c, d); add_point(&pathStr, endC.fX, endC.fY); pathStr.appendf(", "); add_point(&pathStr, endD.fX, endD.fY); pathStr.appendf(");\n"); pathStr.appendf(" path.close();\n"); pathStr.appendf(" pathB.moveTo(%d,%d);\n", c, d); pathStr.appendf(" pathB.cubicTo("); add_point(&pathStr, endC.fX, endC.fY); pathStr.appendf(", "); add_point(&pathStr, endD.fX, endD.fY); pathStr.appendf(", %d,%d);\n", a, b); pathStr.appendf(" pathB.close();\n"); pathStr.appendf(" testPathOp(reporter, path, pathB, kIntersect_SkPathOp," " filename);\n"); pathStr.appendf("}\n"); state.outputProgress(pathStr.c_str(), kIntersect_SkPathOp); } SkString testName; testName.printf("thread_loops%d", ++gLoopsTestNo); testPathOp(state.fReporter, pathA, pathB, kIntersect_SkPathOp, testName.c_str()); if (PathOpsDebug::gCheckForDuplicateNames) return; } } } } }
static sk_sp<SkImageFilter> make_image_filter(bool canBeNull) { sk_sp<SkImageFilter> filter; // Add a 1 in 3 chance to get a nullptr input if (canBeNull && (R(3) == 1)) { return filter; } enum { ALPHA_THRESHOLD, MERGE, COLOR, LUT3D, BLUR, MAGNIFIER, DOWN_SAMPLE, XFERMODE, OFFSET, MATRIX, MATRIX_CONVOLUTION, COMPOSE, DISTANT_LIGHT, POINT_LIGHT, SPOT_LIGHT, NOISE, DROP_SHADOW, MORPHOLOGY, BITMAP, DISPLACE, TILE, PICTURE, PAINT, NUM_FILTERS }; switch (R(NUM_FILTERS)) { case ALPHA_THRESHOLD: filter = SkAlphaThresholdFilter::Make(make_region(), make_scalar(), make_scalar(), make_image_filter()); break; case MERGE: filter = SkMergeImageFilter::Make(make_image_filter(), make_image_filter(), make_xfermode()); break; case COLOR: { sk_sp<SkColorFilter> cf(make_color_filter()); filter = cf ? SkColorFilterImageFilter::Make(std::move(cf), make_image_filter()) : nullptr; break; } case LUT3D: { int cubeDimension; sk_sp<SkData> lut3D(make_3Dlut(&cubeDimension, (R(2) == 1), (R(2) == 1), (R(2) == 1))); sk_sp<SkColorFilter> cf(SkColorCubeFilter::Make(std::move(lut3D), cubeDimension)); filter = cf ? SkColorFilterImageFilter::Make(std::move(cf), make_image_filter()) : nullptr; break; } case BLUR: filter = SkBlurImageFilter::Make(make_scalar(true), make_scalar(true), make_image_filter()); break; case MAGNIFIER: filter = SkMagnifierImageFilter::Make(make_rect(), make_scalar(true), make_image_filter()); break; case DOWN_SAMPLE: filter = SkDownSampleImageFilter::Make(make_scalar(), make_image_filter()); break; case XFERMODE: filter = SkXfermodeImageFilter::Make(SkXfermode::Make(make_xfermode()), make_image_filter(), make_image_filter(), nullptr); break; case OFFSET: filter = SkOffsetImageFilter::Make(make_scalar(), make_scalar(), make_image_filter()); break; case MATRIX: filter = SkImageFilter::MakeMatrixFilter(make_matrix(), (SkFilterQuality)R(4), make_image_filter()); break; case MATRIX_CONVOLUTION: { SkImageFilter::CropRect cropR(SkRect::MakeWH(SkIntToScalar(kBitmapSize), SkIntToScalar(kBitmapSize))); SkISize size = SkISize::Make(R(10)+1, R(10)+1); int arraySize = size.width() * size.height(); SkTArray<SkScalar> kernel(arraySize); for (int i = 0; i < arraySize; ++i) { kernel.push_back() = make_scalar(); } SkIPoint kernelOffset = SkIPoint::Make(R(SkIntToScalar(size.width())), R(SkIntToScalar(size.height()))); filter = SkMatrixConvolutionImageFilter::Make(size, kernel.begin(), make_scalar(), make_scalar(), kernelOffset, (SkMatrixConvolutionImageFilter::TileMode)R(3), R(2) == 1, make_image_filter(), &cropR); break; } case COMPOSE: filter = SkComposeImageFilter::Make(make_image_filter(), make_image_filter()); break; case DISTANT_LIGHT: filter = (R(2) == 1) ? SkLightingImageFilter::MakeDistantLitDiffuse(make_point(), make_color(), make_scalar(), make_scalar(), make_image_filter()) : SkLightingImageFilter::MakeDistantLitSpecular(make_point(), make_color(), make_scalar(), make_scalar(), SkIntToScalar(R(10)), make_image_filter()); break; case POINT_LIGHT: filter = (R(2) == 1) ? SkLightingImageFilter::MakePointLitDiffuse(make_point(), make_color(), make_scalar(), make_scalar(), make_image_filter()) : SkLightingImageFilter::MakePointLitSpecular(make_point(), make_color(), make_scalar(), make_scalar(), SkIntToScalar(R(10)), make_image_filter()); break; case SPOT_LIGHT: filter = (R(2) == 1) ? SkLightingImageFilter::MakeSpotLitDiffuse(SkPoint3::Make(0, 0, 0), make_point(), make_scalar(), make_scalar(), make_color(), make_scalar(), make_scalar(), make_image_filter()) : SkLightingImageFilter::MakeSpotLitSpecular(SkPoint3::Make(0, 0, 0), make_point(), make_scalar(), make_scalar(), make_color(), make_scalar(), make_scalar(), SkIntToScalar(R(10)), make_image_filter()); break; case NOISE: { sk_sp<SkShader> shader((R(2) == 1) ? SkPerlinNoiseShader::MakeFractalNoise(make_scalar(true), make_scalar(true), R(10.0f), make_scalar()) : SkPerlinNoiseShader::MakeTurbulence(make_scalar(true), make_scalar(true), R(10.0f), make_scalar())); SkPaint paint; paint.setShader(shader); SkImageFilter::CropRect cropR(SkRect::MakeWH(SkIntToScalar(kBitmapSize), SkIntToScalar(kBitmapSize))); filter = SkPaintImageFilter::Make(paint, &cropR); break; } case DROP_SHADOW: filter = SkDropShadowImageFilter::Make(make_scalar(), make_scalar(), make_scalar(true), make_scalar(true), make_color(), make_shadow_mode(), make_image_filter(), nullptr); break; case MORPHOLOGY: if (R(2) == 1) { filter = SkDilateImageFilter::Make(R(static_cast<float>(kBitmapSize)), R(static_cast<float>(kBitmapSize)), make_image_filter()); } else { filter = SkErodeImageFilter::Make(R(static_cast<float>(kBitmapSize)), R(static_cast<float>(kBitmapSize)), make_image_filter()); } break; case BITMAP: { sk_sp<SkImage> image(SkImage::MakeFromBitmap(make_bitmap())); if (R(2) == 1) { filter = SkImageSource::Make(std::move(image), make_rect(), make_rect(), kHigh_SkFilterQuality); } else { filter = SkImageSource::Make(std::move(image)); } break; } case DISPLACE: filter = SkDisplacementMapEffect::Make(make_channel_selector_type(), make_channel_selector_type(), make_scalar(), make_image_filter(false), make_image_filter()); break; case TILE: filter = SkTileImageFilter::Make(make_rect(), make_rect(), make_image_filter(false)); break; case PICTURE: { SkRTreeFactory factory; SkPictureRecorder recorder; SkCanvas* recordingCanvas = recorder.beginRecording(SkIntToScalar(kBitmapSize), SkIntToScalar(kBitmapSize), &factory, 0); drawSomething(recordingCanvas); sk_sp<SkPicture> pict(recorder.finishRecordingAsPicture()); filter = SkPictureImageFilter::Make(pict, make_rect()); break; } case PAINT: { SkImageFilter::CropRect cropR(make_rect()); filter = SkPaintImageFilter::Make(make_paint(), &cropR); break; } default: break; } return (filter || canBeNull) ? filter : make_image_filter(canBeNull); }
static GrTexture* copy_on_gpu(GrTexture* inputTexture, const SkIRect* subset, const CopyParams& copyParams) { SkASSERT(!subset || !subset->isEmpty()); GrContext* context = inputTexture->getContext(); SkASSERT(context); const GrCaps* caps = context->caps(); // Either it's a cache miss or the original wasn't cached to begin with. GrSurfaceDesc rtDesc = inputTexture->desc(); rtDesc.fFlags = rtDesc.fFlags | kRenderTarget_GrSurfaceFlag; rtDesc.fWidth = copyParams.fWidth; rtDesc.fHeight = copyParams.fHeight; rtDesc.fConfig = GrMakePixelConfigUncompressed(rtDesc.fConfig); // If the config isn't renderable try converting to either A8 or an 32 bit config. Otherwise, // fail. if (!caps->isConfigRenderable(rtDesc.fConfig, false)) { if (GrPixelConfigIsAlphaOnly(rtDesc.fConfig)) { if (caps->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) { rtDesc.fConfig = kAlpha_8_GrPixelConfig; } else if (caps->isConfigRenderable(kSkia8888_GrPixelConfig, false)) { rtDesc.fConfig = kSkia8888_GrPixelConfig; } else { return nullptr; } } else if (kRGB_GrColorComponentFlags == (kRGB_GrColorComponentFlags & GrPixelConfigComponentMask(rtDesc.fConfig))) { if (caps->isConfigRenderable(kSkia8888_GrPixelConfig, false)) { rtDesc.fConfig = kSkia8888_GrPixelConfig; } else { return nullptr; } } else { return nullptr; } } SkAutoTUnref<GrTexture> copy(context->textureProvider()->createTexture(rtDesc, true)); if (!copy) { return nullptr; } // TODO: If no scaling is being performed then use copySurface. GrPaint paint; // TODO: Initializing these values for no reason cause the compiler is complaining SkScalar sx = 0.f; SkScalar sy = 0.f; if (subset) { sx = 1.f / inputTexture->width(); sy = 1.f / inputTexture->height(); } if (copyParams.fFilter != GrTextureParams::kNone_FilterMode && subset && (subset->width() != copyParams.fWidth || subset->height() != copyParams.fHeight)) { SkRect domain; domain.fLeft = (subset->fLeft + 0.5f) * sx; domain.fTop = (subset->fTop + 0.5f)* sy; domain.fRight = (subset->fRight - 0.5f) * sx; domain.fBottom = (subset->fBottom - 0.5f) * sy; // This would cause us to read values from outside the subset. Surely, the caller knows // better! SkASSERT(copyParams.fFilter != GrTextureParams::kMipMap_FilterMode); paint.addColorFragmentProcessor( GrTextureDomainEffect::Create(inputTexture, SkMatrix::I(), domain, GrTextureDomain::kClamp_Mode, copyParams.fFilter))->unref(); } else { GrTextureParams params(SkShader::kClamp_TileMode, copyParams.fFilter); paint.addColorTextureProcessor(inputTexture, SkMatrix::I(), params); } paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); SkRect localRect; if (subset) { localRect = SkRect::Make(*subset); localRect.fLeft *= sx; localRect.fTop *= sy; localRect.fRight *= sx; localRect.fBottom *= sy; } else { localRect = SkRect::MakeWH(1.f, 1.f); } SkAutoTUnref<GrDrawContext> drawContext(context->drawContext(copy->asRenderTarget())); if (!drawContext) { return nullptr; } SkRect dstRect = SkRect::MakeWH(SkIntToScalar(rtDesc.fWidth), SkIntToScalar(rtDesc.fHeight)); drawContext->fillRectToRect(GrClip::WideOpen(), paint, SkMatrix::I(), dstRect, localRect); return copy.detach(); }
SkImageFilter* SkiaImageFilterBuilder::build(const FilterOperations& operations) { SkAutoTUnref<SkImageFilter> filter; SkScalar matrix[20]; for (size_t i = 0; i < operations.size(); ++i) { const FilterOperation& op = *operations.at(i); switch (op.getOperationType()) { case FilterOperation::REFERENCE: { FilterEffect* filterEffect = static_cast<const ReferenceFilterOperation*>(&op)->filterEffect(); // FIXME: hook up parent filter to image source filter.reset(SkiaImageFilterBuilder::build(filterEffect)); break; } case FilterOperation::GRAYSCALE: { float amount = static_cast<const BasicColorMatrixFilterOperation*>(&op)->amount(); getGrayscaleMatrix(1 - amount, matrix); filter.reset(createMatrixImageFilter(matrix, filter)); break; } case FilterOperation::SEPIA: { float amount = static_cast<const BasicColorMatrixFilterOperation*>(&op)->amount(); getSepiaMatrix(1 - amount, matrix); filter.reset(createMatrixImageFilter(matrix, filter)); break; } case FilterOperation::SATURATE: { float amount = static_cast<const BasicColorMatrixFilterOperation*>(&op)->amount(); getSaturateMatrix(amount, matrix); filter.reset(createMatrixImageFilter(matrix, filter)); break; } case FilterOperation::HUE_ROTATE: { float amount = static_cast<const BasicColorMatrixFilterOperation*>(&op)->amount(); getHueRotateMatrix(amount, matrix); filter.reset(createMatrixImageFilter(matrix, filter)); break; } case FilterOperation::INVERT: { float amount = static_cast<const BasicComponentTransferFilterOperation*>(&op)->amount(); getInvertMatrix(amount, matrix); filter.reset(createMatrixImageFilter(matrix, filter)); break; } case FilterOperation::OPACITY: { float amount = static_cast<const BasicComponentTransferFilterOperation*>(&op)->amount(); getOpacityMatrix(amount, matrix); filter.reset(createMatrixImageFilter(matrix, filter)); break; } case FilterOperation::BRIGHTNESS: { float amount = static_cast<const BasicComponentTransferFilterOperation*>(&op)->amount(); getBrightnessMatrix(amount, matrix); filter.reset(createMatrixImageFilter(matrix, filter)); break; } case FilterOperation::CONTRAST: { float amount = static_cast<const BasicComponentTransferFilterOperation*>(&op)->amount(); getContrastMatrix(amount, matrix); filter.reset(createMatrixImageFilter(matrix, filter)); break; } case FilterOperation::BLUR: { float pixelRadius = static_cast<const BlurFilterOperation*>(&op)->stdDeviation().getFloatValue(); filter.reset(new SkBlurImageFilter(pixelRadius, pixelRadius, filter)); break; } case FilterOperation::DROP_SHADOW: { const DropShadowFilterOperation* drop = static_cast<const DropShadowFilterOperation*>(&op); filter.reset(new DropShadowImageFilter(SkIntToScalar(drop->x()), SkIntToScalar(drop->y()), SkIntToScalar(drop->stdDeviation()), drop->color().rgb(), filter)); break; } case FilterOperation::VALIDATED_CUSTOM: case FilterOperation::CUSTOM: // Not supported. case FilterOperation::PASSTHROUGH: case FilterOperation::NONE: break; } } return filter.detach(); }
static void generateMask(const SkMask& mask, const SkPath& path, const SkMaskGamma::PreBlend& maskPreBlend) { SkPaint paint; int srcW = mask.fBounds.width(); int srcH = mask.fBounds.height(); int dstW = srcW; int dstH = srcH; int dstRB = mask.fRowBytes; SkMatrix matrix; matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft), -SkIntToScalar(mask.fBounds.fTop)); paint.setAntiAlias(SkMask::kBW_Format != mask.fFormat); switch (mask.fFormat) { case SkMask::kBW_Format: dstRB = 0; // signals we need a copy break; case SkMask::kA8_Format: break; case SkMask::kLCD16_Format: case SkMask::kLCD32_Format: // TODO: trigger off LCD orientation dstW = 4*dstW - 8; matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft + 1), -SkIntToScalar(mask.fBounds.fTop)); matrix.postScale(SkIntToScalar(4), SK_Scalar1); dstRB = 0; // signals we need a copy break; default: SkDEBUGFAIL("unexpected mask format"); } SkRasterClip clip; clip.setRect(SkIRect::MakeWH(dstW, dstH)); const SkImageInfo info = SkImageInfo::MakeA8(dstW, dstH); SkBitmap bm; if (0 == dstRB) { if (!bm.tryAllocPixels(info)) { // can't allocate offscreen, so empty the mask and return sk_bzero(mask.fImage, mask.computeImageSize()); return; } } else { bm.installPixels(info, mask.fImage, dstRB); } sk_bzero(bm.getPixels(), bm.getSafeSize()); SkDraw draw; draw.fRC = &clip; draw.fClip = &clip.bwRgn(); draw.fMatrix = &matrix; draw.fBitmap = &bm; draw.drawPath(path, paint); switch (mask.fFormat) { case SkMask::kBW_Format: packA8ToA1(mask, bm.getAddr8(0, 0), bm.rowBytes()); break; case SkMask::kA8_Format: if (maskPreBlend.isApplicable()) { applyLUTToA8Mask(mask, maskPreBlend.fG); } break; case SkMask::kLCD16_Format: if (maskPreBlend.isApplicable()) { pack4xHToLCD16<true>(bm, mask, maskPreBlend); } else { pack4xHToLCD16<false>(bm, mask, maskPreBlend); } break; case SkMask::kLCD32_Format: if (maskPreBlend.isApplicable()) { pack4xHToLCD32<true>(bm, mask, maskPreBlend); } else { pack4xHToLCD32<false>(bm, mask, maskPreBlend); } break; default: break; } }
virtual void onDraw(SkCanvas* canvas) { SkBlurMaskFilter::BlurStyle NONE = SkBlurMaskFilter::BlurStyle(-999); static const struct { SkBlurMaskFilter::BlurStyle fStyle; int fCx, fCy; } gRecs[] = { { NONE, 0, 0 }, { SkBlurMaskFilter::kInner_BlurStyle, -1, 0 }, { SkBlurMaskFilter::kNormal_BlurStyle, 0, 1 }, { SkBlurMaskFilter::kSolid_BlurStyle, 0, -1 }, { SkBlurMaskFilter::kOuter_BlurStyle, 1, 0 }, }; SkPaint paint; paint.setAntiAlias(true); paint.setTextSize(SkIntToScalar(25)); canvas->translate(SkIntToScalar(-40), SkIntToScalar(0)); SkBlurMaskFilter::BlurFlags flags = SkBlurMaskFilter::kNone_BlurFlag; for (int j = 0; j < 2; j++) { canvas->save(); paint.setColor(SK_ColorBLUE); for (size_t i = 0; i < SK_ARRAY_COUNT(gRecs); i++) { if (gRecs[i].fStyle != NONE) { SkMaskFilter* mf = SkBlurMaskFilter::Create( SkIntToScalar(20), gRecs[i].fStyle, flags ); paint.setMaskFilter(mf)->unref(); } else { paint.setMaskFilter(NULL); } canvas->drawCircle(SkIntToScalar(200 + gRecs[i].fCx*100) , SkIntToScalar(200 + gRecs[i].fCy*100) , SkIntToScalar(50) , paint); } // draw text { SkMaskFilter* mf = SkBlurMaskFilter::Create( SkIntToScalar(4) , SkBlurMaskFilter::kNormal_BlurStyle , flags ); paint.setMaskFilter(mf)->unref(); SkScalar x = SkIntToScalar(70); SkScalar y = SkIntToScalar(400); paint.setColor(SK_ColorBLACK); canvas->drawText("Hamburgefons Style", 18, x, y, paint); canvas->drawText("Hamburgefons Style", 18 , x, y + SkIntToScalar(50), paint); paint.setMaskFilter(NULL); paint.setColor(SK_ColorWHITE); x -= SkIntToScalar(2); y -= SkIntToScalar(2); canvas->drawText("Hamburgefons Style", 18, x, y, paint); } canvas->restore(); flags = SkBlurMaskFilter::kHighQuality_BlurFlag; canvas->translate(SkIntToScalar(350), SkIntToScalar(0)); } }
/* * Copyright 2010 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "SkTouchGesture.h" #include "SkMatrix.h" #include "SkTime.h" #define DISCRETIZE_TRANSLATE_TO_AVOID_FLICKER true static const SkScalar MAX_FLING_SPEED = SkIntToScalar(1500); static SkScalar pin_max_fling(SkScalar speed) { if (speed > MAX_FLING_SPEED) { speed = MAX_FLING_SPEED; } return speed; } static double getseconds() { return SkTime::GetMSecs() * 0.001; } // returns +1 or -1, depending on the sign of x // returns +1 if z is zero static SkScalar SkScalarSignNonZero(SkScalar x) {
virtual void onDraw(SkCanvas* canvas) { SkBlurDrawLooper* shadowLoopers[5]; shadowLoopers[0] = SkBlurDrawLooper::Create(SK_ColorBLUE, SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(10)), SkIntToScalar(5), SkIntToScalar(10), SkBlurDrawLooper::kIgnoreTransform_BlurFlag | SkBlurDrawLooper::kOverrideColor_BlurFlag | SkBlurDrawLooper::kHighQuality_BlurFlag); SkAutoUnref aurL0(shadowLoopers[0]); shadowLoopers[1] = SkBlurDrawLooper::Create(SK_ColorBLUE, SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(10)), SkIntToScalar(5), SkIntToScalar(10), SkBlurDrawLooper::kIgnoreTransform_BlurFlag | SkBlurDrawLooper::kOverrideColor_BlurFlag); SkAutoUnref aurL1(shadowLoopers[1]); shadowLoopers[2] = SkBlurDrawLooper::Create(SK_ColorBLACK, SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(5)), SkIntToScalar(5), SkIntToScalar(10), SkBlurDrawLooper::kIgnoreTransform_BlurFlag | SkBlurDrawLooper::kHighQuality_BlurFlag); SkAutoUnref aurL2(shadowLoopers[2]); shadowLoopers[3] = SkBlurDrawLooper::Create(0x7FFF0000, SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(5)), SkIntToScalar(-5), SkIntToScalar(-10), SkBlurDrawLooper::kIgnoreTransform_BlurFlag | SkBlurDrawLooper::kOverrideColor_BlurFlag | SkBlurDrawLooper::kHighQuality_BlurFlag); SkAutoUnref aurL3(shadowLoopers[3]); shadowLoopers[4] = SkBlurDrawLooper::Create(SK_ColorBLACK, SkIntToScalar(0), SkIntToScalar(5), SkIntToScalar(5), SkBlurDrawLooper::kIgnoreTransform_BlurFlag | SkBlurDrawLooper::kOverrideColor_BlurFlag | SkBlurDrawLooper::kHighQuality_BlurFlag); SkAutoUnref aurL4(shadowLoopers[4]); static const struct { SkColor fColor; SkScalar fStrokeWidth; } gRec[] = { { SK_ColorRED, -SK_Scalar1 }, { SK_ColorGREEN, SkIntToScalar(4) }, { SK_ColorBLUE, SkIntToScalar(0)}, }; SkPaint paint; paint.setAntiAlias(true); for (size_t i = 0; i < SK_ARRAY_COUNT(shadowLoopers); ++i) { SkAutoCanvasRestore acr(canvas, true); paint.setLooper(shadowLoopers[i]); canvas->translate(SkIntToScalar((unsigned int)i*40), SkIntToScalar(0)); setup(&paint, gRec[0].fColor, gRec[0].fStrokeWidth); canvas->drawRect(fRect, paint); canvas->translate(SkIntToScalar(0), SkIntToScalar(40)); setup(&paint, gRec[1].fColor, gRec[1].fStrokeWidth); canvas->drawPath(fCirclePath, paint); canvas->translate(SkIntToScalar(0), SkIntToScalar(40)); setup(&paint, gRec[2].fColor, gRec[2].fStrokeWidth); canvas->drawPath(fCirclePath, paint); } }
void WRasterImage::setChanged(WFlags<ChangeFlag> flags) { if (flags & Clipping) { if (painter()->hasClipping()) { impl_->setTransform(painter()->clipPathTransform()); SkPath clipPath; impl_->drawPlainPath(clipPath, painter()->clipPath()); impl_->canvas_->clipPath(clipPath, SkRegion::kReplace_Op); } else { impl_->canvas_->restore(); impl_->canvas_->save(); } impl_->setTransform(painter()->combinedTransform()); } if (flags & Transform) { impl_->setTransform(painter()->combinedTransform()); flags = Pen | Brush | Font | Hints; } if (flags & Hints) { if (!(painter()->renderHints() & WPainter::Antialiasing)) { impl_->strokePaint_.setAntiAlias(false); impl_->fillPaint_.setAntiAlias(false); impl_->textPaint_.setAntiAlias(false); } else { impl_->strokePaint_.setAntiAlias(true); impl_->fillPaint_.setAntiAlias(true); impl_->textPaint_.setAntiAlias(true); } } if (flags & Pen) { const WPen& pen = painter()->pen(); if (pen.style() != NoPen) { const WColor& color = pen.color(); impl_->strokePaint_.setColor(fromWColor(color)); WLength w = pen.width(); impl_->strokePaint_.setStrokeWidth(SkIntToScalar(w.toPixels())); switch (pen.capStyle()) { case FlatCap: impl_->strokePaint_.setStrokeCap(SkPaint::kButt_Cap); break; case SquareCap: impl_->strokePaint_.setStrokeCap(SkPaint::kSquare_Cap); break; case RoundCap: impl_->strokePaint_.setStrokeCap(SkPaint::kRound_Cap); break; } switch (pen.joinStyle()) { case MiterJoin: impl_->strokePaint_.setStrokeJoin(SkPaint::kMiter_Join); break; case BevelJoin: impl_->strokePaint_.setStrokeJoin(SkPaint::kBevel_Join); break; case RoundJoin: impl_->strokePaint_.setStrokeJoin(SkPaint::kRound_Join); break; } SkPathEffect *pe = impl_->strokePaint_.setPathEffect(0); if (pe) pe->unref(); switch (pen.style()) { case NoPen: break; case SolidLine: break; case DashLine: { const SkScalar dasharray[] = { SkIntToScalar(4), SkIntToScalar(2) }; impl_->strokePaint_.setPathEffect(new SkDashPathEffect(dasharray, 2, false))->unref(); break; } case DotLine: { const SkScalar dasharray[] = { SkIntToScalar(1), SkIntToScalar(2) }; impl_->strokePaint_.setPathEffect(new SkDashPathEffect(dasharray, 2, false))->unref(); break; } case DashDotLine: { const SkScalar dasharray[] = { SkIntToScalar(4), SkIntToScalar(2), SkIntToScalar(1), SkIntToScalar(2) }; impl_->strokePaint_.setPathEffect(new SkDashPathEffect(dasharray, 4, false))->unref(); break; } case DashDotDotLine: { const SkScalar dasharray[] = { SkIntToScalar(4), SkIntToScalar(2), SkIntToScalar(1), SkIntToScalar(2), SkIntToScalar(1), SkIntToScalar(2) }; impl_->strokePaint_.setPathEffect(new SkDashPathEffect(dasharray, 6, false))->unref(); break; } } } } if (flags & Brush) { const WBrush& brush = painter()->brush(); if (brush.style() != NoBrush) { const WColor& color = painter()->brush().color(); impl_->fillPaint_.setColor(fromWColor(color)); } } if (flags & Font) { const WFont& font = painter()->font(); const char *base = 0; switch (font.genericFamily()) { case WFont::Default: case WFont::Serif: base = "Times"; break; case WFont::SansSerif: base = "Helvetica"; break; case WFont::Monospace: base = "Courier"; break; case WFont::Fantasy: // Not really ! base = "Symbol"; break; case WFont::Cursive: // Not really ! base = "ZapfDingbats"; } int style = SkTypeface::kNormal; if (font.style() != WFont::NormalStyle) style |= SkTypeface::kItalic; if (font.weight() == WFont::Bold || font.weight() == WFont::Bolder) style |= SkTypeface::kBold; impl_->textPaint_.setTypeface(SkTypeface::CreateFromName(base, (SkTypeface::Style)style))->unref(); impl_->textPaint_.setTextSize(SkIntToScalar(font.sizeLength(12).toPixels())); } }
void onOnceBeforeDraw() override { for (int i = 0; i <= kLastEnum_SkBlurStyle; ++i) { fMaskFilters[i] = SkMaskFilter::MakeBlur((SkBlurStyle)i, SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(STROKE_WIDTH/2))); } }
void SkStroke::strokePath(const SkPath& src, SkPath* dst) const { SkASSERT(&src != NULL && dst != NULL); SkScalar radius = SkScalarHalf(fWidth); dst->reset(); if (radius <= 0) { return; } #ifdef SK_SCALAR_IS_FIXED void (*proc)(SkPoint pts[], int count) = identity_proc; if (needs_to_shrink(src)) { proc = shift_down_2_proc; radius >>= 2; if (radius == 0) { return; } } #endif SkPathStroker stroker(radius, fMiterLimit, this->getCap(), this->getJoin()); SkPath::Iter iter(src, false); SkPoint pts[4]; SkPath::Verb verb, lastSegment = SkPath::kMove_Verb; while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { switch (verb) { case SkPath::kMove_Verb: APPLY_PROC(proc, &pts[0], 1); stroker.moveTo(pts[0]); break; case SkPath::kLine_Verb: APPLY_PROC(proc, &pts[1], 1); stroker.lineTo(pts[1]); lastSegment = verb; break; case SkPath::kQuad_Verb: APPLY_PROC(proc, &pts[1], 2); stroker.quadTo(pts[1], pts[2]); lastSegment = verb; break; case SkPath::kCubic_Verb: APPLY_PROC(proc, &pts[1], 3); stroker.cubicTo(pts[1], pts[2], pts[3]); lastSegment = verb; break; case SkPath::kClose_Verb: stroker.close(lastSegment == SkPath::kLine_Verb); break; default: break; } } stroker.done(dst, lastSegment == SkPath::kLine_Verb); #ifdef SK_SCALAR_IS_FIXED // undo our previous down_shift if (shift_down_2_proc == proc) { // need a real shift methid on path. antialias paths could use this too SkMatrix matrix; matrix.setScale(SkIntToScalar(4), SkIntToScalar(4)); dst->transform(matrix); } #endif if (fDoFill) { if (src.cheapIsDirection(SkPath::kCCW_Direction)) { dst->reverseAddPath(src); } else { dst->addPath(src); } } else { // Seems like we can assume that a 2-point src would always result in // a convex stroke, but testing has proved otherwise. // TODO: fix the stroker to make this assumption true (without making // it slower that the work that will be done in computeConvexity()) #if 0 // this test results in a non-convex stroke :( static void test(SkCanvas* canvas) { SkPoint pts[] = { 146.333328, 192.333328, 300.333344, 293.333344 }; SkPaint paint; paint.setStrokeWidth(7); paint.setStrokeCap(SkPaint::kRound_Cap); canvas->drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, paint); } #endif #if 0 if (2 == src.countPoints()) { dst->setIsConvex(true); } #endif }
//////////////////////////////////////////////////////////////////////////////// // Create a 1-bit clip mask in the stencil buffer. 'devClipBounds' are in device // (as opposed to canvas) coordinates bool GrClipMaskManager::createStencilClipMask(const GrClipData& clipDataIn, const GrIRect& devClipBounds) { GrAssert(kNone_ClipMaskType == fCurrClipMaskType); GrDrawState* drawState = fGpu->drawState(); GrAssert(drawState->isClipState()); GrRenderTarget* rt = drawState->getRenderTarget(); GrAssert(NULL != rt); // TODO: dynamically attach a SB when needed. GrStencilBuffer* stencilBuffer = rt->getStencilBuffer(); if (NULL == stencilBuffer) { return false; } if (stencilBuffer->mustRenderClip(clipDataIn, rt->width(), rt->height())) { stencilBuffer->setLastClip(clipDataIn, rt->width(), rt->height()); // we set the current clip to the bounds so that our recursive // draws are scissored to them. We use the copy of the complex clip // we just stashed on the SB to render from. We set it back after // we finish drawing it into the stencil. const GrClipData* oldClipData = fGpu->getClip(); // The origin of 'newClipData' is (0, 0) so it is okay to place // a device-coordinate bound in 'newClipStack' SkClipStack newClipStack(devClipBounds); GrClipData newClipData; newClipData.fClipStack = &newClipStack; fGpu->setClip(&newClipData); GrDrawTarget::AutoStateRestore asr(fGpu, GrDrawTarget::kReset_ASRInit); drawState = fGpu->drawState(); drawState->setRenderTarget(rt); GrDrawTarget::AutoGeometryPush agp(fGpu); if (0 != clipDataIn.fOrigin.fX || 0 != clipDataIn.fOrigin.fY) { // Add the saveLayer's offset to the view matrix rather than // offset each individual draw drawState->viewMatrix()->setTranslate( SkIntToScalar(-clipDataIn.fOrigin.fX), SkIntToScalar(-clipDataIn.fOrigin.fY)); } #if !VISUALIZE_COMPLEX_CLIP drawState->enableState(GrDrawState::kNoColorWrites_StateBit); #endif int clipBit = stencilBuffer->bits(); SkASSERT((clipBit <= 16) && "Ganesh only handles 16b or smaller stencil buffers"); clipBit = (1 << (clipBit-1)); GrIRect devRTRect = GrIRect::MakeWH(rt->width(), rt->height()); bool clearToInside; SkRegion::Op firstOp = SkRegion::kReplace_Op; // suppress warning SkClipStack::Iter iter(*oldClipData->fClipStack, SkClipStack::Iter::kBottom_IterStart); const SkClipStack::Iter::Clip* clip = process_initial_clip_elements(&iter, devRTRect, &clearToInside, &firstOp, clipDataIn); fGpu->clearStencilClip(devClipBounds, clearToInside); bool first = true; // walk through each clip element and perform its set op // with the existing clip. for ( ; NULL != clip; clip = iter.next()) { GrPathFill fill; bool fillInverted = false; // enabled at bottom of loop drawState->disableState(GrGpu::kModifyStencilClip_StateBit); // if the target is MSAA then we want MSAA enabled when the clip is soft if (rt->isMultisampled()) { if (clip->fDoAA) { drawState->enableState(GrDrawState::kHWAntialias_StateBit); } else { drawState->disableState(GrDrawState::kHWAntialias_StateBit); } } // Can the clip element be drawn directly to the stencil buffer // with a non-inverted fill rule without extra passes to // resolve in/out status? bool canRenderDirectToStencil = false; SkRegion::Op op = clip->fOp; if (first) { first = false; op = firstOp; } GrPathRenderer* pr = NULL; const SkPath* clipPath = NULL; if (NULL != clip->fRect) { canRenderDirectToStencil = true; fill = kEvenOdd_GrPathFill; fillInverted = false; // there is no point in intersecting a screen filling // rectangle. if (SkRegion::kIntersect_Op == op && contains(*clip->fRect, devRTRect, oldClipData->fOrigin)) { continue; } } else { GrAssert(NULL != clip->fPath); fill = get_path_fill(*clip->fPath); fillInverted = GrIsFillInverted(fill); fill = GrNonInvertedFill(fill); clipPath = clip->fPath; pr = this->getContext()->getPathRenderer(*clipPath, fill, fGpu, false, true); if (NULL == pr) { fGpu->setClip(oldClipData); return false; } canRenderDirectToStencil = !pr->requiresStencilPass(*clipPath, fill, fGpu); } int passes; GrStencilSettings stencilSettings[GrStencilSettings::kMaxStencilClipPasses]; bool canDrawDirectToClip; // Given the renderer, the element, // fill rule, and set operation can // we render the element directly to // stencil bit used for clipping. canDrawDirectToClip = GrStencilSettings::GetClipPasses(op, canRenderDirectToStencil, clipBit, fillInverted, &passes, stencilSettings); // draw the element to the client stencil bits if necessary if (!canDrawDirectToClip) { GR_STATIC_CONST_SAME_STENCIL(gDrawToStencil, kIncClamp_StencilOp, kIncClamp_StencilOp, kAlways_StencilFunc, 0xffff, 0x0000, 0xffff); SET_RANDOM_COLOR if (NULL != clip->fRect) { *drawState->stencil() = gDrawToStencil; fGpu->drawSimpleRect(*clip->fRect, NULL); } else { if (canRenderDirectToStencil) { *drawState->stencil() = gDrawToStencil; pr->drawPath(*clipPath, fill, NULL, fGpu, false); } else { pr->drawPathToStencil(*clipPath, fill, fGpu); } } } // now we modify the clip bit by rendering either the clip // element directly or a bounding rect of the entire clip. drawState->enableState(GrGpu::kModifyStencilClip_StateBit); for (int p = 0; p < passes; ++p) { *drawState->stencil() = stencilSettings[p]; if (canDrawDirectToClip) { if (NULL != clip->fRect) { SET_RANDOM_COLOR fGpu->drawSimpleRect(*clip->fRect, NULL); } else { SET_RANDOM_COLOR pr->drawPath(*clipPath, fill, NULL, fGpu, false); } } else { SET_RANDOM_COLOR // 'devClipBounds' is already in device coordinates so the // translation in the view matrix is inappropriate. // Convert it to canvas space so the drawn rect will // be in the correct location GrRect canvClipBounds; canvClipBounds.set(devClipBounds); device_to_canvas(&canvClipBounds, clipDataIn.fOrigin); fGpu->drawSimpleRect(canvClipBounds, NULL); } } }
virtual void onDraw(SkCanvas* canvas) { this->init(); SkPaint paint; SkColorMatrix matrix; paint.setXfermodeMode(SkXfermode::kSrc_Mode); const SkBitmap bmps[] = { fSolidBitmap, fTransparentBitmap }; for (size_t i = 0; i < SK_ARRAY_COUNT(bmps); ++i) { matrix.setIdentity(); setColorMatrix(&paint, matrix); canvas->drawBitmap(bmps[i], 0, 0, &paint); matrix.setRotate(SkColorMatrix::kR_Axis, 90); setColorMatrix(&paint, matrix); canvas->drawBitmap(bmps[i], 80, 0, &paint); matrix.setRotate(SkColorMatrix::kG_Axis, 90); setColorMatrix(&paint, matrix); canvas->drawBitmap(bmps[i], 160, 0, &paint); matrix.setRotate(SkColorMatrix::kB_Axis, 90); setColorMatrix(&paint, matrix); canvas->drawBitmap(bmps[i], 240, 0, &paint); matrix.setSaturation(0.0f); setColorMatrix(&paint, matrix); canvas->drawBitmap(bmps[i], 0, 80, &paint); matrix.setSaturation(0.5f); setColorMatrix(&paint, matrix); canvas->drawBitmap(bmps[i], 80, 80, &paint); matrix.setSaturation(1.0f); setColorMatrix(&paint, matrix); canvas->drawBitmap(bmps[i], 160, 80, &paint); matrix.setSaturation(2.0f); setColorMatrix(&paint, matrix); canvas->drawBitmap(bmps[i], 240, 80, &paint); matrix.setRGB2YUV(); setColorMatrix(&paint, matrix); canvas->drawBitmap(bmps[i], 0, 160, &paint); matrix.setYUV2RGB(); setColorMatrix(&paint, matrix); canvas->drawBitmap(bmps[i], 80, 160, &paint); SkScalar s1 = SK_Scalar1; SkScalar s255 = SkIntToScalar(255); // Move red into alpha, set color to white SkScalar data[20] = { 0, 0, 0, 0, s255, 0, 0, 0, 0, s255, 0, 0, 0, 0, s255, s1, 0, 0, 0, 0, }; setArray(&paint, data); canvas->drawBitmap(bmps[i], 160, 160, &paint); canvas->translate(0, 240); } }