void SkSVGDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap, int x, int y, const SkPaint& paint) { SkMatrix adjustedMatrix = *draw.fMatrix; adjustedMatrix.preTranslate(SkIntToScalar(x), SkIntToScalar(y)); SkDraw adjustedDraw(draw); adjustedDraw.fMatrix = &adjustedMatrix; drawBitmapCommon(adjustedDraw, bitmap, paint); }
bool SkLayerRasterizer::onRasterize(const SkPath& path, const SkMatrix& matrix, const SkIRect* clipBounds, SkMask* mask, SkMask::CreateMode mode) const { SkASSERT(fLayers); if (fLayers->empty()) { return false; } if (SkMask::kJustRenderImage_CreateMode != mode) { if (!compute_bounds(*fLayers, path, matrix, clipBounds, &mask->fBounds)) return false; } if (SkMask::kComputeBoundsAndRenderImage_CreateMode == mode) { mask->fFormat = SkMask::kA8_Format; mask->fRowBytes = mask->fBounds.width(); size_t size = mask->computeImageSize(); if (0 == size) { return false; // too big to allocate, abort } mask->fImage = SkMask::AllocImage(size); memset(mask->fImage, 0, size); } if (SkMask::kJustComputeBounds_CreateMode != mode) { SkBitmap device; SkRasterClip rectClip; SkDraw draw; SkMatrix translatedMatrix; // this translates us to our local pixels SkMatrix drawMatrix; // this translates the path by each layer's offset rectClip.setRect(SkIRect::MakeWH(mask->fBounds.width(), mask->fBounds.height())); translatedMatrix = matrix; translatedMatrix.postTranslate(-SkIntToScalar(mask->fBounds.fLeft), -SkIntToScalar(mask->fBounds.fTop)); device.installMaskPixels(*mask); draw.fBitmap = &device; draw.fMatrix = &drawMatrix; draw.fRC = &rectClip; draw.fClip = &rectClip.bwRgn(); // we set the matrixproc in the loop, as the matrix changes each time (potentially) SkDeque::F2BIter iter(*fLayers); SkLayerRasterizer_Rec* rec; while ((rec = (SkLayerRasterizer_Rec*)iter.next()) != NULL) { drawMatrix = translatedMatrix; drawMatrix.preTranslate(rec->fOffset.fX, rec->fOffset.fY); draw.drawPath(path, rec->fPaint); } } return true; }
bool SkLayerRasterizer::onRasterize(const SkPath& path, const SkMatrix& matrix, const SkIRect* clipBounds, SkMask* mask, SkMask::CreateMode mode) { if (fLayers.empty()) return false; if (SkMask::kJustRenderImage_CreateMode != mode) { if (!compute_bounds(fLayers, path, matrix, clipBounds, &mask->fBounds)) return false; } if (SkMask::kComputeBoundsAndRenderImage_CreateMode == mode) { mask->fFormat = SkMask::kA8_Format; mask->fRowBytes = SkToU16(mask->fBounds.width()); mask->fImage = SkMask::AllocImage(mask->computeImageSize()); memset(mask->fImage, 0, mask->computeImageSize()); } if (SkMask::kJustComputeBounds_CreateMode != mode) { SkBitmap device; SkDraw draw; SkMatrix translatedMatrix; // this translates us to our local pixels SkMatrix drawMatrix; // this translates the path by each layer's offset SkRegion rectClip; rectClip.setRect(0, 0, mask->fBounds.width(), mask->fBounds.height()); translatedMatrix = matrix; translatedMatrix.postTranslate(-SkIntToScalar(mask->fBounds.fLeft), -SkIntToScalar(mask->fBounds.fTop)); device.setConfig(SkBitmap::kA8_Config, mask->fBounds.width(), mask->fBounds.height(), mask->fRowBytes); device.setPixels(mask->fImage); draw.fBitmap = &device; draw.fMatrix = &drawMatrix; draw.fClip = &rectClip; // we set the matrixproc in the loop, as the matrix changes each time (potentially) draw.fBounder = NULL; SkDeque::Iter iter(fLayers); SkLayerRasterizer_Rec* rec; while ((rec = (SkLayerRasterizer_Rec*)iter.next()) != NULL) { drawMatrix = translatedMatrix; drawMatrix.preTranslate(rec->fOffset.fX, rec->fOffset.fY); draw.drawPath(path, rec->fPaint); } } return true; }
static SkBitmap make_bmp(int w, int h) { SkBitmap bmp; bmp.allocN32Pixels(w, h, true); SkCanvas canvas(bmp); SkScalar wScalar = SkIntToScalar(w); SkScalar hScalar = SkIntToScalar(h); SkPoint pt = { wScalar / 2, hScalar / 2 }; SkScalar radius = 3 * SkMaxScalar(wScalar, hScalar); SkColor colors[] = { SK_ColorDKGRAY, 0xFF222255, 0xFF331133, 0xFF884422, 0xFF000022, SK_ColorWHITE, 0xFFAABBCC}; SkScalar pos[] = {0, SK_Scalar1 / 6, 2 * SK_Scalar1 / 6, 3 * SK_Scalar1 / 6, 4 * SK_Scalar1 / 6, 5 * SK_Scalar1 / 6, SK_Scalar1}; SkPaint paint; SkRect rect = SkRect::MakeWH(wScalar, hScalar); SkMatrix mat = SkMatrix::I(); for (int i = 0; i < 4; ++i) { paint.setShader(SkGradientShader::CreateRadial( pt, radius, colors, pos, SK_ARRAY_COUNT(colors), SkShader::kRepeat_TileMode, 0, &mat))->unref(); canvas.drawRect(rect, paint); rect.inset(wScalar / 8, hScalar / 8); mat.preTranslate(6 * wScalar, 6 * hScalar); mat.postScale(SK_Scalar1 / 3, SK_Scalar1 / 3); } paint.setAntiAlias(true); sk_tool_utils::set_portable_typeface(&paint); paint.setTextSize(wScalar / 2.2f); paint.setShader(0); paint.setColor(SK_ColorLTGRAY); static const char kTxt[] = "Skia"; SkPoint texPos = { wScalar / 17, hScalar / 2 + paint.getTextSize() / 2.5f }; canvas.drawText(kTxt, SK_ARRAY_COUNT(kTxt)-1, texPos.fX, texPos.fY, paint); paint.setColor(SK_ColorBLACK); paint.setStyle(SkPaint::kStroke_Style); paint.setStrokeWidth(SK_Scalar1); canvas.drawText(kTxt, SK_ARRAY_COUNT(kTxt)-1, texPos.fX, texPos.fY, paint); return bmp; }
void GrLayerHoister::DrawLayersToAtlas(GrContext* context, const SkTDArray<GrHoistedLayer>& atlased) { if (atlased.count() > 0) { // All the atlased layers are rendered into the same GrTexture SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTargetDirect( atlased[0].fLayer->texture()->asRenderTarget(), NULL)); SkCanvas* atlasCanvas = surface->getCanvas(); SkPaint clearPaint; clearPaint.setColor(SK_ColorTRANSPARENT); clearPaint.setXfermode(SkXfermode::Create(SkXfermode::kSrc_Mode))->unref(); for (int i = 0; i < atlased.count(); ++i) { const GrCachedLayer* layer = atlased[i].fLayer; const SkPicture* pict = atlased[i].fPicture; const SkIPoint offset = atlased[i].fOffset; SkDEBUGCODE(const SkPaint* layerPaint = layer->paint();) SkASSERT(!layerPaint || !layerPaint->getImageFilter()); atlasCanvas->save(); // Add a rect clip to make sure the rendering doesn't // extend beyond the boundaries of the atlased sub-rect SkRect bound = SkRect::MakeXYWH(SkIntToScalar(layer->rect().fLeft), SkIntToScalar(layer->rect().fTop), SkIntToScalar(layer->rect().width()), SkIntToScalar(layer->rect().height())); atlasCanvas->clipRect(bound); // Since 'clear' doesn't respect the clip we need to draw a rect atlasCanvas->drawRect(bound, clearPaint); // '-offset' maps the layer's top/left to the origin. // Since this layer is atlased, the top/left corner needs // to be offset to the correct location in the backing texture. SkMatrix initialCTM; initialCTM.setTranslate(SkIntToScalar(-offset.fX), SkIntToScalar(-offset.fY)); initialCTM.preTranslate(bound.fLeft, bound.fTop); initialCTM.preConcat(atlased[i].fPreMat); atlasCanvas->setMatrix(initialCTM); atlasCanvas->concat(atlased[i].fLocalMat); SkRecordPartialDraw(*pict->fRecord.get(), atlasCanvas, bound, layer->start() + 1, layer->stop(), initialCTM); atlasCanvas->restore(); } atlasCanvas->flush(); }
void GrLayerHoister::DrawLayersToAtlas(GrContext* context, const SkTDArray<GrHoistedLayer>& atlased) { if (atlased.count() > 0) { // All the atlased layers are rendered into the same GrTexture SkSurfaceProps props(0, kUnknown_SkPixelGeometry); SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTargetDirect( atlased[0].fLayer->texture()->asRenderTarget(), &props)); SkCanvas* atlasCanvas = surface->getCanvas(); for (int i = 0; i < atlased.count(); ++i) { const GrCachedLayer* layer = atlased[i].fLayer; const SkBigPicture* pict = atlased[i].fPicture->asSkBigPicture(); if (!pict) { // TODO: can we assume / assert this? continue; } const SkIPoint offset = SkIPoint::Make(layer->srcIR().fLeft, layer->srcIR().fTop); SkDEBUGCODE(const SkPaint* layerPaint = layer->paint();) SkASSERT(!layerPaint || !layerPaint->getImageFilter()); SkASSERT(!layer->filter()); atlasCanvas->save(); // Add a rect clip to make sure the rendering doesn't // extend beyond the boundaries of the atlased sub-rect const SkRect bound = SkRect::Make(layer->rect()); atlasCanvas->clipRect(bound); atlasCanvas->clear(0); // '-offset' maps the layer's top/left to the origin. // Since this layer is atlased, the top/left corner needs // to be offset to the correct location in the backing texture. SkMatrix initialCTM; initialCTM.setTranslate(SkIntToScalar(-offset.fX), SkIntToScalar(-offset.fY)); initialCTM.preTranslate(bound.fLeft, bound.fTop); initialCTM.preConcat(atlased[i].fPreMat); atlasCanvas->setMatrix(initialCTM); atlasCanvas->concat(atlased[i].fLocalMat); pict->partialPlayback(atlasCanvas, layer->start() + 1, layer->stop(), initialCTM); atlasCanvas->restore(); } atlasCanvas->flush(); }
//////////////////////////////////////////////////////////////////////////////// // set up the draw state to enable the aa clipping mask. Besides setting up the // stage matrix this also alters the vertex layout static const GrFragmentProcessor* create_fp_for_mask(GrTexture* result, const SkIRect &devBound) { SkMatrix mat; // We use device coords to compute the texture coordinates. We set our matrix to be a // translation to the devBound, and then a scaling matrix to normalized coords. mat.setIDiv(result->width(), result->height()); mat.preTranslate(SkIntToScalar(-devBound.fLeft), SkIntToScalar(-devBound.fTop)); SkIRect domainTexels = SkIRect::MakeWH(devBound.width(), devBound.height()); return GrTextureDomainEffect::Create(result, mat, GrTextureDomain::MakeTexelDomain(result, domainTexels), GrTextureDomain::kDecal_Mode, GrTextureParams::kNone_FilterMode, kDevice_GrCoordSet); }
void draw(SkCanvas* canvas) { SkPaint paint; SkPictureRecorder recorder; SkCanvas* recordingCanvas = recorder.beginRecording(50, 50); for (auto color : { SK_ColorRED, SK_ColorBLUE, 0xff007f00 } ) { paint.setColor(color); recordingCanvas->drawRect({10, 10, 30, 40}, paint); recordingCanvas->translate(10, 10); recordingCanvas->scale(1.2f, 1.4f); } sk_sp<SkPicture> playback = recorder.finishRecordingAsPicture(); const SkPicture* playbackPtr = playback.get(); SkMatrix matrix; matrix.reset(); for (auto alpha : { 70, 140, 210 } ) { paint.setAlpha(alpha); canvas->drawPicture(playbackPtr, &matrix, &paint); matrix.preTranslate(70, 70); } }
void GrSWMaskHelper::DrawToTargetWithPathMask(GrTexture* texture, GrDrawTarget* target, const GrIRect& rect) { GrDrawState* drawState = target->drawState(); GrDrawState::AutoDeviceCoordDraw adcd(drawState); if (!adcd.succeeded()) { return; } enum { // the SW path renderer shares this stage with glyph // rendering (kGlyphMaskStage in GrTextContext) // && edge rendering (kEdgeEffectStage in GrContext) kPathMaskStage = GrPaint::kTotalStages, }; GrRect dstRect = GrRect::MakeLTRB( SK_Scalar1 * rect.fLeft, SK_Scalar1 * rect.fTop, SK_Scalar1 * rect.fRight, SK_Scalar1 * rect.fBottom); // We want to use device coords to compute the texture coordinates. We set our matrix to be // equal to the view matrix followed by a translation so that the top-left of the device bounds // maps to 0,0, and then a scaling matrix to normalized coords. We apply this matrix to the // vertex positions rather than local coords. SkMatrix maskMatrix; maskMatrix.setIDiv(texture->width(), texture->height()); maskMatrix.preTranslate(SkIntToScalar(-rect.fLeft), SkIntToScalar(-rect.fTop)); maskMatrix.preConcat(drawState->getViewMatrix()); GrAssert(!drawState->isStageEnabled(kPathMaskStage)); drawState->setEffect(kPathMaskStage, GrSimpleTextureEffect::Create(texture, maskMatrix, false, GrEffect::kPosition_CoordsType))->unref(); target->drawSimpleRect(dstRect); drawState->disableStage(kPathMaskStage); }
static void morphpoints(SkPoint dst[], const SkPoint src[], int count, SkPathMeasure& meas, SkScalar dist) { for (int i = 0; i < count; i++) { SkPoint pos; SkVector tangent; SkScalar sx = src[i].fX; SkScalar sy = src[i].fY; meas.getPosTan(dist + 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); } }
static bool compute_bounds(const SkDeque& layers, const SkPath& path, const SkMatrix& matrix, const SkIRect* clipBounds, SkIRect* bounds) { SkDeque::F2BIter iter(layers); SkLayerRasterizer_Rec* rec; bounds->set(SK_MaxS32, SK_MaxS32, SK_MinS32, SK_MinS32); while ((rec = (SkLayerRasterizer_Rec*)iter.next()) != NULL) { const SkPaint& paint = rec->fPaint; SkPath fillPath, devPath; const SkPath* p = &path; if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) { paint.getFillPath(path, &fillPath); p = &fillPath; } if (p->isEmpty()) { continue; } // apply the matrix and offset { SkMatrix m = matrix; m.preTranslate(rec->fOffset.fX, rec->fOffset.fY); p->transform(m, &devPath); } SkMask mask; if (!SkDraw::DrawToMask(devPath, clipBounds, paint.getMaskFilter(), &matrix, &mask, SkMask::kJustComputeBounds_CreateMode, SkPaint::kFill_Style)) { return false; } bounds->join(mask.fBounds); } return true; }
void GrSWMaskHelper::DrawToTargetWithPathMask(GrTexture* texture, GrDrawContext* drawContext, const GrPaint* paint, const GrUserStencilSettings* userStencilSettings, const GrClip& clip, GrColor color, const SkMatrix& viewMatrix, const SkIRect& rect) { SkMatrix invert; if (!viewMatrix.invert(&invert)) { return; } SkRect dstRect = SkRect::MakeLTRB(SK_Scalar1 * rect.fLeft, SK_Scalar1 * rect.fTop, SK_Scalar1 * rect.fRight, SK_Scalar1 * rect.fBottom); // We use device coords to compute the texture coordinates. We take the device coords and apply // a translation so that the top-left of the device bounds maps to 0,0, and then a scaling // matrix to normalized coords. SkMatrix maskMatrix; maskMatrix.setIDiv(texture->width(), texture->height()); maskMatrix.preTranslate(SkIntToScalar(-rect.fLeft), SkIntToScalar(-rect.fTop)); GrPipelineBuilder pipelineBuilder(*paint, drawContext->isUnifiedMultisampled()); pipelineBuilder.setRenderTarget(drawContext->accessRenderTarget()); pipelineBuilder.setUserStencil(userStencilSettings); pipelineBuilder.addCoverageFragmentProcessor( GrSimpleTextureEffect::Create(texture, maskMatrix, GrTextureParams::kNone_FilterMode, kDevice_GrCoordSet))->unref(); SkAutoTUnref<GrDrawBatch> batch(GrRectBatchFactory::CreateNonAAFill(color, SkMatrix::I(), dstRect, nullptr, &invert)); drawContext->drawBatch(pipelineBuilder, clip, batch); }
void SkBaseDevice::drawTextBlob(const SkDraw& draw, const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint &paint) { SkMatrix localMatrix; SkDraw localDraw(draw); if (x || y) { localMatrix = *draw.fMatrix; localMatrix.preTranslate(x, y); localDraw.fMatrix = &localMatrix; } SkPaint runPaint = paint; SkTextBlob::RunIterator it(blob); while (!it.done()) { size_t textLen = it.glyphCount() * sizeof(uint16_t); const SkPoint& offset = it.offset(); // applyFontToPaint() always overwrites the exact same attributes, // so it is safe to not re-seed the paint. it.applyFontToPaint(&runPaint); switch (it.positioning()) { case SkTextBlob::kDefault_Positioning: this->drawText(localDraw, it.glyphs(), textLen, offset.x(), offset.y(), runPaint); break; case SkTextBlob::kHorizontal_Positioning: case SkTextBlob::kFull_Positioning: this->drawPosText(localDraw, it.glyphs(), textLen, it.pos(), offset.y(), SkTextBlob::ScalarsPerGlyph(it.positioning()), runPaint); break; default: SkFAIL("unhandled positioning mode"); } it.next(); } }
SkPDFImageShader::SkPDFImageShader(SkPDFShader::State* state) : fState(state) { fState.get()->fImage.lockPixels(); SkMatrix finalMatrix = fState.get()->fCanvasTransform; finalMatrix.preConcat(fState.get()->fShaderTransform); SkRect surfaceBBox; surfaceBBox.set(fState.get()->fBBox); transformBBox(finalMatrix, &surfaceBBox); SkMatrix unflip; unflip.setTranslate(0, SkScalarRound(surfaceBBox.height())); unflip.preScale(SK_Scalar1, -SK_Scalar1); SkISize size = SkISize::Make(SkScalarRound(surfaceBBox.width()), SkScalarRound(surfaceBBox.height())); SkPDFDevice pattern(size, size, unflip); SkCanvas canvas(&pattern); canvas.translate(-surfaceBBox.fLeft, -surfaceBBox.fTop); finalMatrix.preTranslate(surfaceBBox.fLeft, surfaceBBox.fTop); const SkBitmap* image = &fState.get()->fImage; int width = image->width(); int height = image->height(); SkShader::TileMode tileModes[2]; tileModes[0] = fState.get()->fImageTileModes[0]; tileModes[1] = fState.get()->fImageTileModes[1]; canvas.drawBitmap(*image, 0, 0); SkRect patternBBox = SkRect::MakeXYWH(-surfaceBBox.fLeft, -surfaceBBox.fTop, width, height); // Tiling is implied. First we handle mirroring. if (tileModes[0] == SkShader::kMirror_TileMode) { SkMatrix xMirror; xMirror.setScale(-1, 1); xMirror.postTranslate(2 * width, 0); canvas.drawBitmapMatrix(*image, xMirror); patternBBox.fRight += width; } if (tileModes[1] == SkShader::kMirror_TileMode) { SkMatrix yMirror; yMirror.setScale(SK_Scalar1, -SK_Scalar1); yMirror.postTranslate(0, 2 * height); canvas.drawBitmapMatrix(*image, yMirror); patternBBox.fBottom += height; } if (tileModes[0] == SkShader::kMirror_TileMode && tileModes[1] == SkShader::kMirror_TileMode) { SkMatrix mirror; mirror.setScale(-1, -1); mirror.postTranslate(2 * width, 2 * height); canvas.drawBitmapMatrix(*image, mirror); } // Then handle Clamping, which requires expanding the pattern canvas to // cover the entire surfaceBBox. // If both x and y are in clamp mode, we start by filling in the corners. // (Which are just a rectangles of the corner colors.) if (tileModes[0] == SkShader::kClamp_TileMode && tileModes[1] == SkShader::kClamp_TileMode) { SkPaint paint; SkRect rect; rect = SkRect::MakeLTRB(surfaceBBox.fLeft, surfaceBBox.fTop, 0, 0); if (!rect.isEmpty()) { paint.setColor(image->getColor(0, 0)); canvas.drawRect(rect, paint); } rect = SkRect::MakeLTRB(width, surfaceBBox.fTop, surfaceBBox.fRight, 0); if (!rect.isEmpty()) { paint.setColor(image->getColor(width - 1, 0)); canvas.drawRect(rect, paint); } rect = SkRect::MakeLTRB(width, height, surfaceBBox.fRight, surfaceBBox.fBottom); if (!rect.isEmpty()) { paint.setColor(image->getColor(width - 1, height - 1)); canvas.drawRect(rect, paint); } rect = SkRect::MakeLTRB(surfaceBBox.fLeft, height, 0, surfaceBBox.fBottom); if (!rect.isEmpty()) { paint.setColor(image->getColor(0, height - 1)); canvas.drawRect(rect, paint); } } // Then expand the left, right, top, then bottom. if (tileModes[0] == SkShader::kClamp_TileMode) { SkIRect subset = SkIRect::MakeXYWH(0, 0, 1, height); if (surfaceBBox.fLeft < 0) { SkBitmap left; SkAssertResult(image->extractSubset(&left, subset)); SkMatrix leftMatrix; leftMatrix.setScale(-surfaceBBox.fLeft, 1); leftMatrix.postTranslate(surfaceBBox.fLeft, 0); canvas.drawBitmapMatrix(left, leftMatrix); if (tileModes[1] == SkShader::kMirror_TileMode) { leftMatrix.postScale(SK_Scalar1, -SK_Scalar1); leftMatrix.postTranslate(0, 2 * height); canvas.drawBitmapMatrix(left, leftMatrix); } patternBBox.fLeft = 0; } if (surfaceBBox.fRight > width) { SkBitmap right; subset.offset(width - 1, 0); SkAssertResult(image->extractSubset(&right, subset)); SkMatrix rightMatrix; rightMatrix.setScale(surfaceBBox.fRight - width, 1); rightMatrix.postTranslate(width, 0); canvas.drawBitmapMatrix(right, rightMatrix); if (tileModes[1] == SkShader::kMirror_TileMode) { rightMatrix.postScale(SK_Scalar1, -SK_Scalar1); rightMatrix.postTranslate(0, 2 * height); canvas.drawBitmapMatrix(right, rightMatrix); } patternBBox.fRight = surfaceBBox.width(); } } if (tileModes[1] == SkShader::kClamp_TileMode) { SkIRect subset = SkIRect::MakeXYWH(0, 0, width, 1); if (surfaceBBox.fTop < 0) { SkBitmap top; SkAssertResult(image->extractSubset(&top, subset)); SkMatrix topMatrix; topMatrix.setScale(SK_Scalar1, -surfaceBBox.fTop); topMatrix.postTranslate(0, surfaceBBox.fTop); canvas.drawBitmapMatrix(top, topMatrix); if (tileModes[0] == SkShader::kMirror_TileMode) { topMatrix.postScale(-1, 1); topMatrix.postTranslate(2 * width, 0); canvas.drawBitmapMatrix(top, topMatrix); } patternBBox.fTop = 0; } if (surfaceBBox.fBottom > height) { SkBitmap bottom; subset.offset(0, height - 1); SkAssertResult(image->extractSubset(&bottom, subset)); SkMatrix bottomMatrix; bottomMatrix.setScale(SK_Scalar1, surfaceBBox.fBottom - height); bottomMatrix.postTranslate(0, height); canvas.drawBitmapMatrix(bottom, bottomMatrix); if (tileModes[0] == SkShader::kMirror_TileMode) { bottomMatrix.postScale(-1, 1); bottomMatrix.postTranslate(2 * width, 0); canvas.drawBitmapMatrix(bottom, bottomMatrix); } patternBBox.fBottom = surfaceBBox.height(); } } SkRefPtr<SkPDFArray> patternBBoxArray = new SkPDFArray; patternBBoxArray->unref(); // SkRefPtr and new both took a reference. patternBBoxArray->reserve(4); patternBBoxArray->appendScalar(patternBBox.fLeft); patternBBoxArray->appendScalar(patternBBox.fTop); patternBBoxArray->appendScalar(patternBBox.fRight); patternBBoxArray->appendScalar(patternBBox.fBottom); // Put the canvas into the pattern stream (fContent). SkRefPtr<SkStream> content = pattern.content(); content->unref(); // SkRefPtr and content() both took a reference. pattern.getResources(&fResources); setData(content.get()); insertName("Type", "Pattern"); insertInt("PatternType", 1); insertInt("PaintType", 1); insertInt("TilingType", 1); insert("BBox", patternBBoxArray.get()); insertScalar("XStep", patternBBox.width()); insertScalar("YStep", patternBBox.height()); insert("Resources", pattern.getResourceDict()); insert("Matrix", SkPDFUtils::MatrixToArray(finalMatrix))->unref(); fState.get()->fImage.unlockPixels(); }
sk_sp<SkSpecialImage> SkDisplacementMapEffect::onFilterImage(SkSpecialImage* source, const Context& ctx, SkIPoint* offset) const { SkIPoint colorOffset = SkIPoint::Make(0, 0); sk_sp<SkSpecialImage> color(this->filterInput(1, source, ctx, &colorOffset)); if (!color) { return nullptr; } SkIPoint displOffset = SkIPoint::Make(0, 0); sk_sp<SkSpecialImage> displ(this->filterInput(0, source, ctx, &displOffset)); if (!displ) { return nullptr; } const SkIRect srcBounds = SkIRect::MakeXYWH(colorOffset.x(), colorOffset.y(), color->width(), color->height()); // Both paths do bounds checking on color pixel access, we don't need to // pad the color bitmap to bounds here. SkIRect bounds; if (!this->applyCropRect(ctx, srcBounds, &bounds)) { return nullptr; } SkIRect displBounds; displ = this->applyCropRect(ctx, displ.get(), &displOffset, &displBounds); if (!displ) { return nullptr; } if (!bounds.intersect(displBounds)) { return nullptr; } const SkIRect colorBounds = bounds.makeOffset(-colorOffset.x(), -colorOffset.y()); SkVector scale = SkVector::Make(fScale, fScale); ctx.ctm().mapVectors(&scale, 1); #if SK_SUPPORT_GPU if (source->isTextureBacked()) { GrContext* context = source->getContext(); sk_sp<GrTexture> colorTexture(color->asTextureRef(context)); sk_sp<GrTexture> displTexture(displ->asTextureRef(context)); if (!colorTexture || !displTexture) { return nullptr; } GrSurfaceDesc desc; desc.fFlags = kRenderTarget_GrSurfaceFlag; desc.fWidth = bounds.width(); desc.fHeight = bounds.height(); desc.fConfig = kSkia8888_GrPixelConfig; SkAutoTUnref<GrTexture> dst(context->textureProvider()->createApproxTexture(desc)); if (!dst) { return nullptr; } GrPaint paint; SkMatrix offsetMatrix = GrCoordTransform::MakeDivByTextureWHMatrix(displTexture.get()); offsetMatrix.preTranslate(SkIntToScalar(colorOffset.fX - displOffset.fX), SkIntToScalar(colorOffset.fY - displOffset.fY)); paint.addColorFragmentProcessor( GrDisplacementMapEffect::Create(fXChannelSelector, fYChannelSelector, scale, displTexture.get(), offsetMatrix, colorTexture.get(), SkISize::Make(color->width(), color->height())))->unref(); paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); SkMatrix matrix; matrix.setTranslate(-SkIntToScalar(colorBounds.x()), -SkIntToScalar(colorBounds.y())); SkAutoTUnref<GrDrawContext> drawContext(context->drawContext(dst->asRenderTarget())); if (!drawContext) { return nullptr; } drawContext->drawRect(GrClip::WideOpen(), paint, matrix, SkRect::Make(colorBounds)); offset->fX = bounds.left(); offset->fY = bounds.top(); return SkSpecialImage::MakeFromGpu(SkIRect::MakeWH(bounds.width(), bounds.height()), kNeedNewImageUniqueID_SpecialImage, dst); } #endif SkBitmap colorBM, displBM; if (!color->getROPixels(&colorBM) || !displ->getROPixels(&displBM)) { return nullptr; } if ((colorBM.colorType() != kN32_SkColorType) || (displBM.colorType() != kN32_SkColorType)) { return nullptr; } SkAutoLockPixels colorLock(colorBM), displLock(displBM); if (!colorBM.getPixels() || !displBM.getPixels()) { return nullptr; } SkImageInfo info = SkImageInfo::MakeN32(bounds.width(), bounds.height(), colorBM.alphaType()); SkBitmap dst; if (!dst.tryAllocPixels(info)) { return nullptr; } SkAutoLockPixels dstLock(dst); computeDisplacement(fXChannelSelector, fYChannelSelector, scale, &dst, displBM, colorOffset - displOffset, colorBM, colorBounds); offset->fX = bounds.left(); offset->fY = bounds.top(); return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(bounds.width(), bounds.height()), dst); }
bool SkXfermodeImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx, SkBitmap* result, SkIPoint* offset) const { SkBitmap background = src; SkIPoint backgroundOffset = SkIPoint::Make(0, 0); if (getInput(0) && !getInput(0)->getInputResultGPU(proxy, src, ctx, &background, &backgroundOffset)) { return onFilterImage(proxy, src, ctx, result, offset); } GrTexture* backgroundTex = background.getTexture(); SkBitmap foreground = src; SkIPoint foregroundOffset = SkIPoint::Make(0, 0); if (getInput(1) && !getInput(1)->getInputResultGPU(proxy, src, ctx, &foreground, &foregroundOffset)) { return onFilterImage(proxy, src, ctx, result, offset); } GrTexture* foregroundTex = foreground.getTexture(); GrContext* context = foregroundTex->getContext(); GrEffect* xferEffect = NULL; GrTextureDesc desc; desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit; desc.fWidth = src.width(); desc.fHeight = src.height(); desc.fConfig = kSkia8888_GrPixelConfig; GrAutoScratchTexture ast(context, desc); if (NULL == ast.texture()) { return false; } SkAutoTUnref<GrTexture> dst(ast.detach()); GrContext::AutoRenderTarget art(context, dst->asRenderTarget()); if (!fMode || !fMode->asNewEffect(&xferEffect, backgroundTex)) { // canFilterImageGPU() should've taken care of this SkASSERT(false); return false; } SkMatrix foregroundMatrix = GrCoordTransform::MakeDivByTextureWHMatrix(foregroundTex); foregroundMatrix.preTranslate(SkIntToScalar(backgroundOffset.fX-foregroundOffset.fX), SkIntToScalar(backgroundOffset.fY-foregroundOffset.fY)); SkRect srcRect; src.getBounds(&srcRect); GrPaint paint; paint.addColorTextureEffect(foregroundTex, foregroundMatrix); paint.addColorEffect(xferEffect)->unref(); context->drawRect(paint, srcRect); offset->fX = backgroundOffset.fX; offset->fY = backgroundOffset.fY; WrapTexture(dst, src.width(), src.height(), result); return true; }
bool SkXfermodeImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx, SkBitmap* result, SkIPoint* offset) const { SkBitmap background = src; SkIPoint backgroundOffset = SkIPoint::Make(0, 0); if (this->getInput(0) && !this->getInput(0)->getInputResultGPU(proxy, src, ctx, &background, &backgroundOffset)) { return this->onFilterImage(proxy, src, ctx, result, offset); } GrTexture* backgroundTex = background.getTexture(); if (NULL == backgroundTex) { SkASSERT(false); return false; } SkBitmap foreground = src; SkIPoint foregroundOffset = SkIPoint::Make(0, 0); if (this->getInput(1) && !this->getInput(1)->getInputResultGPU(proxy, src, ctx, &foreground, &foregroundOffset)) { return this->onFilterImage(proxy, src, ctx, result, offset); } GrTexture* foregroundTex = foreground.getTexture(); GrContext* context = foregroundTex->getContext(); GrFragmentProcessor* xferProcessor = NULL; GrSurfaceDesc desc; desc.fFlags = kRenderTarget_GrSurfaceFlag; desc.fWidth = src.width(); desc.fHeight = src.height(); desc.fConfig = kSkia8888_GrPixelConfig; SkAutoTUnref<GrTexture> dst(context->textureProvider()->refScratchTexture( desc, GrTextureProvider::kApprox_ScratchTexMatch)); if (!dst) { return false; } GrPaint paint; if (!fMode || !fMode->asFragmentProcessor(&xferProcessor, paint.getProcessorDataManager(), backgroundTex)) { // canFilterImageGPU() should've taken care of this SkASSERT(false); return false; } SkMatrix foregroundMatrix = GrCoordTransform::MakeDivByTextureWHMatrix(foregroundTex); foregroundMatrix.preTranslate(SkIntToScalar(backgroundOffset.fX-foregroundOffset.fX), SkIntToScalar(backgroundOffset.fY-foregroundOffset.fY)); SkRect srcRect; src.getBounds(&srcRect); SkAutoTUnref<GrFragmentProcessor> foregroundDomain(GrTextureDomainEffect::Create( paint.getProcessorDataManager(), foregroundTex, foregroundMatrix, GrTextureDomain::MakeTexelDomain(foregroundTex, foreground.bounds()), GrTextureDomain::kDecal_Mode, GrTextureParams::kNone_FilterMode) ); paint.addColorProcessor(foregroundDomain.get()); paint.addColorProcessor(xferProcessor)->unref(); GrDrawContext* drawContext = context->drawContext(); if (!drawContext) { return false; } drawContext->drawRect(dst->asRenderTarget(), GrClip::WideOpen(), paint, SkMatrix::I(), srcRect); offset->fX = backgroundOffset.fX; offset->fY = backgroundOffset.fY; WrapTexture(dst, src.width(), src.height(), result); return true; }
SkPDFImageShader* SkPDFImageShader::Create( SkPDFCanon* canon, SkScalar dpi, SkAutoTDelete<SkPDFShader::State>* autoState) { const SkPDFShader::State& state = **autoState; state.fImage.lockPixels(); // The image shader pattern cell will be drawn into a separate device // in pattern cell space (no scaling on the bitmap, though there may be // translations so that all content is in the device, coordinates > 0). // Map clip bounds to shader space to ensure the device is large enough // to handle fake clamping. SkMatrix finalMatrix = state.fCanvasTransform; finalMatrix.preConcat(state.fShaderTransform); SkRect deviceBounds; deviceBounds.set(state.fBBox); if (!inverse_transform_bbox(finalMatrix, &deviceBounds)) { return NULL; } const SkBitmap* image = &state.fImage; SkRect bitmapBounds; image->getBounds(&bitmapBounds); // For tiling modes, the bounds should be extended to include the bitmap, // otherwise the bitmap gets clipped out and the shader is empty and awful. // For clamp modes, we're only interested in the clip region, whether // or not the main bitmap is in it. SkShader::TileMode tileModes[2]; tileModes[0] = state.fImageTileModes[0]; tileModes[1] = state.fImageTileModes[1]; if (tileModes[0] != SkShader::kClamp_TileMode || tileModes[1] != SkShader::kClamp_TileMode) { deviceBounds.join(bitmapBounds); } SkISize size = SkISize::Make(SkScalarRoundToInt(deviceBounds.width()), SkScalarRoundToInt(deviceBounds.height())); SkAutoTUnref<SkPDFDevice> patternDevice( SkPDFDevice::CreateUnflipped(size, dpi, canon)); SkCanvas canvas(patternDevice.get()); SkRect patternBBox; image->getBounds(&patternBBox); // Translate the canvas so that the bitmap origin is at (0, 0). canvas.translate(-deviceBounds.left(), -deviceBounds.top()); patternBBox.offset(-deviceBounds.left(), -deviceBounds.top()); // Undo the translation in the final matrix finalMatrix.preTranslate(deviceBounds.left(), deviceBounds.top()); // If the bitmap is out of bounds (i.e. clamp mode where we only see the // stretched sides), canvas will clip this out and the extraneous data // won't be saved to the PDF. canvas.drawBitmap(*image, 0, 0); SkScalar width = SkIntToScalar(image->width()); SkScalar height = SkIntToScalar(image->height()); // Tiling is implied. First we handle mirroring. if (tileModes[0] == SkShader::kMirror_TileMode) { SkMatrix xMirror; xMirror.setScale(-1, 1); xMirror.postTranslate(2 * width, 0); drawBitmapMatrix(&canvas, *image, xMirror); patternBBox.fRight += width; } if (tileModes[1] == SkShader::kMirror_TileMode) { SkMatrix yMirror; yMirror.setScale(SK_Scalar1, -SK_Scalar1); yMirror.postTranslate(0, 2 * height); drawBitmapMatrix(&canvas, *image, yMirror); patternBBox.fBottom += height; } if (tileModes[0] == SkShader::kMirror_TileMode && tileModes[1] == SkShader::kMirror_TileMode) { SkMatrix mirror; mirror.setScale(-1, -1); mirror.postTranslate(2 * width, 2 * height); drawBitmapMatrix(&canvas, *image, mirror); } // Then handle Clamping, which requires expanding the pattern canvas to // cover the entire surfaceBBox. // If both x and y are in clamp mode, we start by filling in the corners. // (Which are just a rectangles of the corner colors.) if (tileModes[0] == SkShader::kClamp_TileMode && tileModes[1] == SkShader::kClamp_TileMode) { SkPaint paint; SkRect rect; rect = SkRect::MakeLTRB(deviceBounds.left(), deviceBounds.top(), 0, 0); if (!rect.isEmpty()) { paint.setColor(image->getColor(0, 0)); canvas.drawRect(rect, paint); } rect = SkRect::MakeLTRB(width, deviceBounds.top(), deviceBounds.right(), 0); if (!rect.isEmpty()) { paint.setColor(image->getColor(image->width() - 1, 0)); canvas.drawRect(rect, paint); } rect = SkRect::MakeLTRB(width, height, deviceBounds.right(), deviceBounds.bottom()); if (!rect.isEmpty()) { paint.setColor(image->getColor(image->width() - 1, image->height() - 1)); canvas.drawRect(rect, paint); } rect = SkRect::MakeLTRB(deviceBounds.left(), height, 0, deviceBounds.bottom()); if (!rect.isEmpty()) { paint.setColor(image->getColor(0, image->height() - 1)); canvas.drawRect(rect, paint); } } // Then expand the left, right, top, then bottom. if (tileModes[0] == SkShader::kClamp_TileMode) { SkIRect subset = SkIRect::MakeXYWH(0, 0, 1, image->height()); if (deviceBounds.left() < 0) { SkBitmap left; SkAssertResult(image->extractSubset(&left, subset)); SkMatrix leftMatrix; leftMatrix.setScale(-deviceBounds.left(), 1); leftMatrix.postTranslate(deviceBounds.left(), 0); drawBitmapMatrix(&canvas, left, leftMatrix); if (tileModes[1] == SkShader::kMirror_TileMode) { leftMatrix.postScale(SK_Scalar1, -SK_Scalar1); leftMatrix.postTranslate(0, 2 * height); drawBitmapMatrix(&canvas, left, leftMatrix); } patternBBox.fLeft = 0; } if (deviceBounds.right() > width) { SkBitmap right; subset.offset(image->width() - 1, 0); SkAssertResult(image->extractSubset(&right, subset)); SkMatrix rightMatrix; rightMatrix.setScale(deviceBounds.right() - width, 1); rightMatrix.postTranslate(width, 0); drawBitmapMatrix(&canvas, right, rightMatrix); if (tileModes[1] == SkShader::kMirror_TileMode) { rightMatrix.postScale(SK_Scalar1, -SK_Scalar1); rightMatrix.postTranslate(0, 2 * height); drawBitmapMatrix(&canvas, right, rightMatrix); } patternBBox.fRight = deviceBounds.width(); } } if (tileModes[1] == SkShader::kClamp_TileMode) { SkIRect subset = SkIRect::MakeXYWH(0, 0, image->width(), 1); if (deviceBounds.top() < 0) { SkBitmap top; SkAssertResult(image->extractSubset(&top, subset)); SkMatrix topMatrix; topMatrix.setScale(SK_Scalar1, -deviceBounds.top()); topMatrix.postTranslate(0, deviceBounds.top()); drawBitmapMatrix(&canvas, top, topMatrix); if (tileModes[0] == SkShader::kMirror_TileMode) { topMatrix.postScale(-1, 1); topMatrix.postTranslate(2 * width, 0); drawBitmapMatrix(&canvas, top, topMatrix); } patternBBox.fTop = 0; } if (deviceBounds.bottom() > height) { SkBitmap bottom; subset.offset(0, image->height() - 1); SkAssertResult(image->extractSubset(&bottom, subset)); SkMatrix bottomMatrix; bottomMatrix.setScale(SK_Scalar1, deviceBounds.bottom() - height); bottomMatrix.postTranslate(0, height); drawBitmapMatrix(&canvas, bottom, bottomMatrix); if (tileModes[0] == SkShader::kMirror_TileMode) { bottomMatrix.postScale(-1, 1); bottomMatrix.postTranslate(2 * width, 0); drawBitmapMatrix(&canvas, bottom, bottomMatrix); } patternBBox.fBottom = deviceBounds.height(); } } // Put the canvas into the pattern stream (fContent). SkAutoTDelete<SkStreamAsset> content(patternDevice->content()); SkPDFImageShader* imageShader = SkNEW_ARGS(SkPDFImageShader, (autoState->detach())); imageShader->setData(content.get()); SkAutoTUnref<SkPDFDict> resourceDict( patternDevice->createResourceDict()); populate_tiling_pattern_dict(imageShader, patternBBox, resourceDict.get(), finalMatrix); imageShader->fShaderState->fImage.unlockPixels(); canon->addImageShader(imageShader); return imageShader; }
void SkBitmapDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap, const SkRect* src, const SkRect& dst, const SkPaint& paint, SkCanvas::DrawBitmapRectFlags flags) { SkMatrix matrix; SkRect bitmapBounds, tmpSrc, tmpDst; SkBitmap tmpBitmap; bitmapBounds.isetWH(bitmap.width(), bitmap.height()); // Compute matrix from the two rectangles if (src) { tmpSrc = *src; } else { tmpSrc = bitmapBounds; } matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit); const SkRect* dstPtr = &dst; const SkBitmap* bitmapPtr = &bitmap; // clip the tmpSrc to the bounds of the bitmap, and recompute dstRect if // needed (if the src was clipped). No check needed if src==null. if (src) { if (!bitmapBounds.contains(*src)) { if (!tmpSrc.intersect(bitmapBounds)) { return; // nothing to draw } // recompute dst, based on the smaller tmpSrc matrix.mapRect(&tmpDst, tmpSrc); dstPtr = &tmpDst; } // since we may need to clamp to the borders of the src rect within // the bitmap, we extract a subset. SkIRect srcIR; tmpSrc.roundOut(&srcIR); if (!bitmap.extractSubset(&tmpBitmap, srcIR)) { return; } bitmapPtr = &tmpBitmap; // Since we did an extract, we need to adjust the matrix accordingly SkScalar dx = 0, dy = 0; if (srcIR.fLeft > 0) { dx = SkIntToScalar(srcIR.fLeft); } if (srcIR.fTop > 0) { dy = SkIntToScalar(srcIR.fTop); } if (dx || dy) { matrix.preTranslate(dx, dy); } SkRect extractedBitmapBounds; extractedBitmapBounds.isetWH(bitmapPtr->width(), bitmapPtr->height()); if (extractedBitmapBounds == tmpSrc) { // no fractional part in src, we can just call drawBitmap goto USE_DRAWBITMAP; } } else { USE_DRAWBITMAP: // We can go faster by just calling drawBitmap, which will concat the // matrix with the CTM, and try to call drawSprite if it can. If not, // it will make a shader and call drawRect, as we do below. this->drawBitmap(draw, *bitmapPtr, matrix, paint); return; } // construct a shader, so we can call drawRect with the dst SkShader* s = SkShader::CreateBitmapShader(*bitmapPtr, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, &matrix); if (NULL == s) { return; } SkPaint paintWithShader(paint); paintWithShader.setStyle(SkPaint::kFill_Style); paintWithShader.setShader(s)->unref(); // Call ourself, in case the subclass wanted to share this setup code // but handle the drawRect code themselves. this->drawRect(draw, *dstPtr, paintWithShader); }
sk_sp<SkSpecialImage> SkXfermodeImageFilter::filterImageGPU(SkSpecialImage* source, sk_sp<SkSpecialImage> background, const SkIPoint& backgroundOffset, sk_sp<SkSpecialImage> foreground, const SkIPoint& foregroundOffset, const SkIRect& bounds) const { SkASSERT(source->isTextureBacked()); GrContext* context = source->getContext(); sk_sp<GrTexture> backgroundTex, foregroundTex; if (background) { backgroundTex = background->asTextureRef(context); } if (foreground) { foregroundTex = foreground->asTextureRef(context); } GrPaint paint; // SRGBTODO: AllowSRGBInputs? sk_sp<GrFragmentProcessor> bgFP; if (backgroundTex) { SkMatrix backgroundMatrix; backgroundMatrix.setIDiv(backgroundTex->width(), backgroundTex->height()); backgroundMatrix.preTranslate(SkIntToScalar(-backgroundOffset.fX), SkIntToScalar(-backgroundOffset.fY)); bgFP = GrTextureDomainEffect::Make( backgroundTex.get(), nullptr, backgroundMatrix, GrTextureDomain::MakeTexelDomain(backgroundTex.get(), background->subset()), GrTextureDomain::kDecal_Mode, GrTextureParams::kNone_FilterMode); } else { bgFP = GrConstColorProcessor::Make(GrColor_TRANSPARENT_BLACK, GrConstColorProcessor::kIgnore_InputMode); } if (foregroundTex) { SkMatrix foregroundMatrix; foregroundMatrix.setIDiv(foregroundTex->width(), foregroundTex->height()); foregroundMatrix.preTranslate(SkIntToScalar(-foregroundOffset.fX), SkIntToScalar(-foregroundOffset.fY)); sk_sp<GrFragmentProcessor> foregroundFP; foregroundFP = GrTextureDomainEffect::Make( foregroundTex.get(), nullptr, foregroundMatrix, GrTextureDomain::MakeTexelDomain(foregroundTex.get(), foreground->subset()), GrTextureDomain::kDecal_Mode, GrTextureParams::kNone_FilterMode); paint.addColorFragmentProcessor(std::move(foregroundFP)); // A null fMode is interpreted to mean kSrcOver_Mode (to match raster). SkAutoTUnref<SkXfermode> mode(SkSafeRef(fMode.get())); if (!mode) { // It would be awesome to use SkXfermode::Create here but it knows better // than us and won't return a kSrcOver_Mode SkXfermode. That means we // have to get one the hard way. struct ProcCoeff rec; rec.fProc = SkXfermode::GetProc(SkXfermode::kSrcOver_Mode); SkXfermode::ModeAsCoeff(SkXfermode::kSrcOver_Mode, &rec.fSC, &rec.fDC); mode.reset(new SkProcCoeffXfermode(rec, SkXfermode::kSrcOver_Mode)); } sk_sp<GrFragmentProcessor> xferFP( mode->makeFragmentProcessorForImageFilter(std::move(bgFP))); // A null 'xferFP' here means kSrc_Mode was used in which case we can just proceed if (xferFP) { paint.addColorFragmentProcessor(std::move(xferFP)); } } else { paint.addColorFragmentProcessor(std::move(bgFP)); } paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); sk_sp<GrDrawContext> drawContext(context->makeDrawContext(SkBackingFit::kApprox, bounds.width(), bounds.height(), kSkia8888_GrPixelConfig, sk_ref_sp(source->getColorSpace()))); if (!drawContext) { return nullptr; } SkMatrix matrix; matrix.setTranslate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top())); drawContext->drawRect(GrNoClip(), paint, matrix, SkRect::Make(bounds)); return SkSpecialImage::MakeFromGpu(SkIRect::MakeWH(bounds.width(), bounds.height()), kNeedNewImageUniqueID_SpecialImage, drawContext->asTexture(), sk_ref_sp(drawContext->getColorSpace())); }
bool SVGPaintServerGradient::setup(GraphicsContext*& context, const RenderObject* object, SVGPaintTargetType type, bool isPaintingText) const { m_ownerElement->buildGradient(); RenderStyle* style = object->style(); bool isFilled = (type & ApplyToFillTargetType) && style->svgStyle()->hasFill(); bool isStroked = (type & ApplyToStrokeTargetType) && style->svgStyle()->hasStroke(); if(!gradientStops().size()) return false; if(gradientStops().size()==1) { context->setFillColor(gradientStops()[0].second); return true; } // Create a gradient builder helper to generate the data // we'll need to provide Skia SkiaGradientBuilder builder(gradientStops(), isFilled ? style->svgStyle()->fillOpacity() : style->svgStyle()->strokeOpacity()); SkShader::TileMode tile_mode; // Convert SVG spread modes to Skia tile modes switch(spreadMethod()) { default: case SPREADMETHOD_PAD: tile_mode = SkShader::kClamp_TileMode; break; case SPREADMETHOD_REFLECT: tile_mode = SkShader::kMirror_TileMode; break; case SPREADMETHOD_REPEAT: tile_mode = SkShader::kRepeat_TileMode; break; } SkShader* shader = NULL; SkMatrix matrix; // Calculate a matrix to transform a gradient to fit the bounding box if (boundingBoxMode()) { matrix.reset(); SkRect rc = context->getBoundingBoxForCurrentPath(true); matrix.preTranslate(rc.fLeft, rc.fTop); matrix.preScale(rc.width(), rc.height()); matrix.preConcat(gradientTransform()); } else matrix = gradientTransform(); if (this->type() == LinearGradientPaintServer) { const SVGPaintServerLinearGradient* linear = static_cast<const SVGPaintServerLinearGradient*>(this); SkPoint pts[2]; pts[0].fX = linear->gradientStart().x(); pts[0].fY = linear->gradientStart().y(); pts[1].fX = linear->gradientEnd().x(); pts[1].fY = linear->gradientEnd().y(); shader = SkGradientShader::CreateLinear(pts, builder.colors(), builder.pos(), builder.count(), tile_mode); } else if (this->type() == RadialGradientPaintServer) { const SVGPaintServerRadialGradient* radial = static_cast<const SVGPaintServerRadialGradient*>(this); SkPoint center; SkScalar radius; center.fX = radial->gradientCenter().x(); center.fY = radial->gradientCenter().y(); radius = radial->gradientRadius(); shader = SkGradientShader::CreateRadial( center, radius, builder.colors(), builder.pos(), builder.count(), tile_mode); } else { return false; } if (isPaintingText) { if (isFilled) { context->setTextDrawingMode(cTextFill); } if (isStroked) { context->setTextDrawingMode(cTextStroke); } } if (isStroked) { applyStrokeStyleToContext(context, style, object); } if (shader) { shader->setLocalMatrix(matrix); context->platformContext()->setGradient(shader); return true; } return false; }
sk_sp<SkShader> Gradient::createShader(const SkMatrix& localMatrix) { sortStopsIfNecessary(); ASSERT(m_stopsSorted); size_t countUsed = totalStopsNeeded(m_stops.data(), m_stops.size()); ASSERT(countUsed >= 2); ASSERT(countUsed >= m_stops.size()); ColorStopOffsetVector pos(countUsed); ColorStopColorVector colors(countUsed); fillStops(m_stops.data(), m_stops.size(), pos, colors); SkShader::TileMode tile = SkShader::kClamp_TileMode; switch (m_spreadMethod) { case SpreadMethodReflect: tile = SkShader::kMirror_TileMode; break; case SpreadMethodRepeat: tile = SkShader::kRepeat_TileMode; break; case SpreadMethodPad: tile = SkShader::kClamp_TileMode; break; } sk_sp<SkShader> shader; uint32_t shouldDrawInPMColorSpace = m_drawInPMColorSpace ? SkGradientShader::kInterpolateColorsInPremul_Flag : 0; if (m_radial) { SkMatrix adjustedLocalMatrix = localMatrix; if (m_aspectRatio != 1) { // CSS3 elliptical gradients: apply the elliptical scaling at the // gradient center point. adjustedLocalMatrix.preTranslate(m_p0.x(), m_p0.y()); adjustedLocalMatrix.preScale(1, 1 / m_aspectRatio); adjustedLocalMatrix.preTranslate(-m_p0.x(), -m_p0.y()); ASSERT(m_p0 == m_p1); } // Since the two-point radial gradient is slower than the plain radial, // only use it if we have to. if (m_p0 == m_p1 && m_r0 <= 0.0f) { shader = SkGradientShader::MakeRadial( m_p1.data(), m_r1, colors.data(), pos.data(), static_cast<int>(countUsed), tile, shouldDrawInPMColorSpace, &adjustedLocalMatrix); } else { // The radii we give to Skia must be positive. If we're given a // negative radius, ask for zero instead. SkScalar radius0 = m_r0 >= 0.0f ? WebCoreFloatToSkScalar(m_r0) : 0; SkScalar radius1 = m_r1 >= 0.0f ? WebCoreFloatToSkScalar(m_r1) : 0; shader = SkGradientShader::MakeTwoPointConical( m_p0.data(), radius0, m_p1.data(), radius1, colors.data(), pos.data(), static_cast<int>(countUsed), tile, shouldDrawInPMColorSpace, &adjustedLocalMatrix); } } else { SkPoint pts[2] = {m_p0.data(), m_p1.data()}; shader = SkGradientShader::MakeLinear( pts, colors.data(), pos.data(), static_cast<int>(countUsed), tile, shouldDrawInPMColorSpace, &localMatrix); } if (!shader) { // use last color, since our "geometry" was degenerate (e.g. radius==0) shader = SkShader::MakeColorShader(colors[countUsed - 1]); } return shader; }
static void SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, TempBitmap& aTmpBitmap, Float aAlpha = 1.0) { switch (aPattern.GetType()) { case PatternType::COLOR: { Color color = static_cast<const ColorPattern&>(aPattern).mColor; aPaint.setColor(ColorToSkColor(color, aAlpha)); break; } case PatternType::LINEAR_GRADIENT: { const LinearGradientPattern& pat = static_cast<const LinearGradientPattern&>(aPattern); GradientStopsSkia *stops = static_cast<GradientStopsSkia*>(pat.mStops.get()); SkShader::TileMode mode = ExtendModeToTileMode(stops->mExtendMode); if (stops->mCount >= 2) { SkPoint points[2]; points[0] = SkPoint::Make(SkFloatToScalar(pat.mBegin.x), SkFloatToScalar(pat.mBegin.y)); points[1] = SkPoint::Make(SkFloatToScalar(pat.mEnd.x), SkFloatToScalar(pat.mEnd.y)); SkShader* shader = SkGradientShader::CreateLinear(points, &stops->mColors.front(), &stops->mPositions.front(), stops->mCount, mode); if (shader) { SkMatrix mat; GfxMatrixToSkiaMatrix(pat.mMatrix, mat); SkShader* matrixShader = SkShader::CreateLocalMatrixShader(shader, mat); SkSafeUnref(shader); SkSafeUnref(aPaint.setShader(matrixShader)); } } else { aPaint.setColor(SkColorSetARGB(0, 0, 0, 0)); } break; } case PatternType::RADIAL_GRADIENT: { const RadialGradientPattern& pat = static_cast<const RadialGradientPattern&>(aPattern); GradientStopsSkia *stops = static_cast<GradientStopsSkia*>(pat.mStops.get()); SkShader::TileMode mode = ExtendModeToTileMode(stops->mExtendMode); if (stops->mCount >= 2) { SkPoint points[2]; points[0] = SkPoint::Make(SkFloatToScalar(pat.mCenter1.x), SkFloatToScalar(pat.mCenter1.y)); points[1] = SkPoint::Make(SkFloatToScalar(pat.mCenter2.x), SkFloatToScalar(pat.mCenter2.y)); SkShader* shader = SkGradientShader::CreateTwoPointConical(points[0], SkFloatToScalar(pat.mRadius1), points[1], SkFloatToScalar(pat.mRadius2), &stops->mColors.front(), &stops->mPositions.front(), stops->mCount, mode); if (shader) { SkMatrix mat; GfxMatrixToSkiaMatrix(pat.mMatrix, mat); SkShader* matrixShader = SkShader::CreateLocalMatrixShader(shader, mat); SkSafeUnref(shader); SkSafeUnref(aPaint.setShader(matrixShader)); } } else { aPaint.setColor(SkColorSetARGB(0, 0, 0, 0)); } break; } case PatternType::SURFACE: { const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern); aTmpBitmap = GetBitmapForSurface(pat.mSurface); SkBitmap& bitmap = aTmpBitmap.mBitmap; SkMatrix mat; GfxMatrixToSkiaMatrix(pat.mMatrix, mat); if (!pat.mSamplingRect.IsEmpty()) { SkIRect rect = IntRectToSkIRect(pat.mSamplingRect); bitmap.extractSubset(&bitmap, rect); mat.preTranslate(rect.x(), rect.y()); } SkShader::TileMode mode = ExtendModeToTileMode(pat.mExtendMode); SkShader* shader = SkShader::CreateBitmapShader(bitmap, mode, mode); SkShader* matrixShader = SkShader::CreateLocalMatrixShader(shader, mat); SkSafeUnref(shader); SkSafeUnref(aPaint.setShader(matrixShader)); if (pat.mFilter == Filter::POINT) { aPaint.setFilterLevel(SkPaint::kNone_FilterLevel); } break; } } }
bool SkXfermodeImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx, SkBitmap* result, SkIPoint* offset) const { SkBitmap background = src; SkIPoint backgroundOffset = SkIPoint::Make(0, 0); if (!this->filterInputGPU(0, proxy, src, ctx, &background, &backgroundOffset)) { return false; } GrTexture* backgroundTex = background.getTexture(); if (nullptr == backgroundTex) { SkASSERT(false); return false; } SkBitmap foreground = src; SkIPoint foregroundOffset = SkIPoint::Make(0, 0); if (!this->filterInputGPU(1, proxy, src, ctx, &foreground, &foregroundOffset)) { return false; } GrTexture* foregroundTex = foreground.getTexture(); GrContext* context = foregroundTex->getContext(); SkIRect bounds = background.bounds().makeOffset(backgroundOffset.x(), backgroundOffset.y()); bounds.join(foreground.bounds().makeOffset(foregroundOffset.x(), foregroundOffset.y())); if (bounds.isEmpty()) { return false; } const GrFragmentProcessor* xferFP = nullptr; GrSurfaceDesc desc; desc.fFlags = kRenderTarget_GrSurfaceFlag; desc.fWidth = bounds.width(); desc.fHeight = bounds.height(); desc.fConfig = kSkia8888_GrPixelConfig; SkAutoTUnref<GrTexture> dst(context->textureProvider()->createApproxTexture(desc)); if (!dst) { return false; } GrPaint paint; SkMatrix backgroundMatrix; backgroundMatrix.setIDiv(backgroundTex->width(), backgroundTex->height()); backgroundMatrix.preTranslate(SkIntToScalar(-backgroundOffset.fX), SkIntToScalar(-backgroundOffset.fY)); SkAutoTUnref<const GrFragmentProcessor> bgFP(GrTextureDomainEffect::Create( backgroundTex, backgroundMatrix, GrTextureDomain::MakeTexelDomain(backgroundTex, background.bounds()), GrTextureDomain::kDecal_Mode, GrTextureParams::kNone_FilterMode) ); if (!fMode || !fMode->asFragmentProcessor(&xferFP, bgFP)) { // canFilterImageGPU() should've taken care of this SkASSERT(false); return false; } SkMatrix foregroundMatrix; foregroundMatrix.setIDiv(foregroundTex->width(), foregroundTex->height()); foregroundMatrix.preTranslate(SkIntToScalar(-foregroundOffset.fX), SkIntToScalar(-foregroundOffset.fY)); SkAutoTUnref<const GrFragmentProcessor> foregroundFP(GrTextureDomainEffect::Create( foregroundTex, foregroundMatrix, GrTextureDomain::MakeTexelDomain(foregroundTex, foreground.bounds()), GrTextureDomain::kDecal_Mode, GrTextureParams::kNone_FilterMode) ); paint.addColorFragmentProcessor(foregroundFP.get()); if (xferFP) { paint.addColorFragmentProcessor(xferFP)->unref(); } paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); SkAutoTUnref<GrDrawContext> drawContext(context->drawContext(dst->asRenderTarget())); if (!drawContext) { return false; } SkMatrix matrix; matrix.setTranslate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top())); drawContext->drawRect(GrClip::WideOpen(), paint, matrix, SkRect::Make(bounds)); offset->fX = bounds.left(); offset->fY = bounds.top(); GrWrapTextureInBitmap(dst, bounds.width(), bounds.height(), false, result); return true; }
static SkMatrix make_div_and_translate_matrix(GrTexture* texture, int x, int y) { SkMatrix matrix = GrCoordTransform::MakeDivByTextureWHMatrix(texture); matrix.preTranslate(SkIntToScalar(x), SkIntToScalar(y)); return matrix; }
static void preTranslate(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx, jfloat dy) { SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); obj->preTranslate(dx, dy); }
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->refScratchTexture(desc, GrContext::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; }
void SkBitmapDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap, const SkRect* src, const SkRect& dst, const SkPaint& paint, SkCanvas::SrcRectConstraint constraint) { SkMatrix matrix; SkRect bitmapBounds, tmpSrc, tmpDst; SkBitmap tmpBitmap; bitmapBounds.isetWH(bitmap.width(), bitmap.height()); // Compute matrix from the two rectangles if (src) { tmpSrc = *src; } else { tmpSrc = bitmapBounds; } matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit); LogDrawScaleFactor(SkMatrix::Concat(*draw.fMatrix, matrix), paint.getFilterQuality()); const SkRect* dstPtr = &dst; const SkBitmap* bitmapPtr = &bitmap; // clip the tmpSrc to the bounds of the bitmap, and recompute dstRect if // needed (if the src was clipped). No check needed if src==null. if (src) { if (!bitmapBounds.contains(*src)) { if (!tmpSrc.intersect(bitmapBounds)) { return; // nothing to draw } // recompute dst, based on the smaller tmpSrc matrix.mapRect(&tmpDst, tmpSrc); dstPtr = &tmpDst; } } if (src && !src->contains(bitmapBounds) && SkCanvas::kFast_SrcRectConstraint == constraint && paint.getFilterQuality() != kNone_SkFilterQuality) { // src is smaller than the bounds of the bitmap, and we are filtering, so we don't know // how much more of the bitmap we need, so we can't use extractSubset or drawBitmap, // but we must use a shader w/ dst bounds (which can access all of the bitmap needed). goto USE_SHADER; } if (src) { // since we may need to clamp to the borders of the src rect within // the bitmap, we extract a subset. const SkIRect srcIR = tmpSrc.roundOut(); if (!bitmap.extractSubset(&tmpBitmap, srcIR)) { return; } bitmapPtr = &tmpBitmap; // Since we did an extract, we need to adjust the matrix accordingly SkScalar dx = 0, dy = 0; if (srcIR.fLeft > 0) { dx = SkIntToScalar(srcIR.fLeft); } if (srcIR.fTop > 0) { dy = SkIntToScalar(srcIR.fTop); } if (dx || dy) { matrix.preTranslate(dx, dy); } SkRect extractedBitmapBounds; extractedBitmapBounds.isetWH(bitmapPtr->width(), bitmapPtr->height()); if (extractedBitmapBounds == tmpSrc) { // no fractional part in src, we can just call drawBitmap goto USE_DRAWBITMAP; } } else { USE_DRAWBITMAP: // We can go faster by just calling drawBitmap, which will concat the // matrix with the CTM, and try to call drawSprite if it can. If not, // it will make a shader and call drawRect, as we do below. if (CanApplyDstMatrixAsCTM(matrix, paint)) { draw.drawBitmap(*bitmapPtr, matrix, dstPtr, paint); return; } } USE_SHADER: // Since the shader need only live for our stack-frame, pass in a custom allocator. This // can save malloc calls, and signals to SkMakeBitmapShader to not try to copy the bitmap // if its mutable, since that precaution is not needed (give the short lifetime of the shader). SkTBlitterAllocator allocator; // construct a shader, so we can call drawRect with the dst auto s = SkMakeBitmapShader(*bitmapPtr, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, &matrix, kNever_SkCopyPixelsMode, &allocator); if (!s) { return; } // we deliberately add a ref, since the allocator wants to be the last owner s.get()->ref(); SkPaint paintWithShader(paint); paintWithShader.setStyle(SkPaint::kFill_Style); paintWithShader.setShader(s); // Call ourself, in case the subclass wanted to share this setup code // but handle the drawRect code themselves. this->drawRect(draw, *dstPtr, paintWithShader); }
bool SkXfermodeImageFilter::filterImageGPUDeprecated(Proxy* proxy, const SkBitmap& src, const Context& ctx, SkBitmap* result, SkIPoint* offset) const { GrContext* context = nullptr; SkBitmap background = src; SkIPoint backgroundOffset = SkIPoint::Make(0, 0); if (!this->filterInputGPUDeprecated(0, proxy, src, ctx, &background, &backgroundOffset)) { background.reset(); } GrTexture* backgroundTex = background.getTexture(); if (backgroundTex) { context = backgroundTex->getContext(); } SkBitmap foreground = src; SkIPoint foregroundOffset = SkIPoint::Make(0, 0); if (!this->filterInputGPUDeprecated(1, proxy, src, ctx, &foreground, &foregroundOffset)) { foreground.reset(); } GrTexture* foregroundTex = foreground.getTexture(); if (foregroundTex) { context = foregroundTex->getContext(); } if (!context) { return false; } SkIRect bounds = background.bounds().makeOffset(backgroundOffset.x(), backgroundOffset.y()); bounds.join(foreground.bounds().makeOffset(foregroundOffset.x(), foregroundOffset.y())); if (bounds.isEmpty()) { return false; } GrSurfaceDesc desc; desc.fFlags = kRenderTarget_GrSurfaceFlag; desc.fWidth = bounds.width(); desc.fHeight = bounds.height(); desc.fConfig = kSkia8888_GrPixelConfig; SkAutoTUnref<GrTexture> dst(context->textureProvider()->createApproxTexture(desc)); if (!dst) { return false; } GrPaint paint; SkAutoTUnref<const GrFragmentProcessor> bgFP; if (backgroundTex) { SkMatrix backgroundMatrix; backgroundMatrix.setIDiv(backgroundTex->width(), backgroundTex->height()); backgroundMatrix.preTranslate(SkIntToScalar(-backgroundOffset.fX), SkIntToScalar(-backgroundOffset.fY)); bgFP.reset(GrTextureDomainEffect::Create( backgroundTex, backgroundMatrix, GrTextureDomain::MakeTexelDomain(backgroundTex, background.bounds()), GrTextureDomain::kDecal_Mode, GrTextureParams::kNone_FilterMode)); } else { bgFP.reset(GrConstColorProcessor::Create(GrColor_TRANSPARENT_BLACK, GrConstColorProcessor::kIgnore_InputMode)); } if (foregroundTex) { SkMatrix foregroundMatrix; foregroundMatrix.setIDiv(foregroundTex->width(), foregroundTex->height()); foregroundMatrix.preTranslate(SkIntToScalar(-foregroundOffset.fX), SkIntToScalar(-foregroundOffset.fY)); SkAutoTUnref<const GrFragmentProcessor> foregroundFP; foregroundFP.reset(GrTextureDomainEffect::Create( foregroundTex, foregroundMatrix, GrTextureDomain::MakeTexelDomain(foregroundTex, foreground.bounds()), GrTextureDomain::kDecal_Mode, GrTextureParams::kNone_FilterMode)); paint.addColorFragmentProcessor(foregroundFP.get()); // A null fMode is interpreted to mean kSrcOver_Mode (to match raster). SkAutoTUnref<SkXfermode> mode(SkSafeRef(fMode.get())); if (!mode) { // It would be awesome to use SkXfermode::Create here but it knows better // than us and won't return a kSrcOver_Mode SkXfermode. That means we // have to get one the hard way. struct ProcCoeff rec; rec.fProc = SkXfermode::GetProc(SkXfermode::kSrcOver_Mode); SkXfermode::ModeAsCoeff(SkXfermode::kSrcOver_Mode, &rec.fSC, &rec.fDC); mode.reset(new SkProcCoeffXfermode(rec, SkXfermode::kSrcOver_Mode)); } SkAutoTUnref<const GrFragmentProcessor> xferFP(mode->getFragmentProcessorForImageFilter(bgFP)); // A null 'xferFP' here means kSrc_Mode was used in which case we can just proceed if (xferFP) { paint.addColorFragmentProcessor(xferFP); } } else { paint.addColorFragmentProcessor(bgFP); } paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); SkAutoTUnref<GrDrawContext> drawContext(context->drawContext(dst->asRenderTarget())); if (!drawContext) { return false; } SkMatrix matrix; matrix.setTranslate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top())); drawContext->drawRect(GrClip::WideOpen(), paint, matrix, SkRect::Make(bounds)); offset->fX = bounds.left(); offset->fY = bounds.top(); GrWrapTextureInBitmap(dst, bounds.width(), bounds.height(), false, result); return true; }
bool SkXfermodeImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm, SkBitmap* result, SkIPoint* offset) { SkBitmap background; SkIPoint backgroundOffset = SkIPoint::Make(0, 0); if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, ctm, &background, &backgroundOffset)) { return false; } GrTexture* backgroundTex = background.getTexture(); SkBitmap foreground; SkIPoint foregroundOffset = SkIPoint::Make(0, 0); if (!SkImageFilterUtils::GetInputResultGPU(getInput(1), proxy, src, ctm, &foreground, &foregroundOffset)) { return false; } GrTexture* foregroundTex = foreground.getTexture(); GrContext* context = foregroundTex->getContext(); GrEffectRef* xferEffect = NULL; GrTextureDesc desc; desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit; desc.fWidth = src.width(); desc.fHeight = src.height(); desc.fConfig = kSkia8888_GrPixelConfig; GrAutoScratchTexture ast(context, desc); SkAutoTUnref<GrTexture> dst(ast.detach()); GrContext::AutoRenderTarget art(context, dst->asRenderTarget()); SkXfermode::Coeff sm, dm; if (!SkXfermode::AsNewEffectOrCoeff(fMode, &xferEffect, &sm, &dm, backgroundTex)) { return false; } SkMatrix foregroundMatrix = GrEffect::MakeDivByTextureWHMatrix(foregroundTex); foregroundMatrix.preTranslate(SkIntToScalar(backgroundOffset.fX-foregroundOffset.fX), SkIntToScalar(backgroundOffset.fY-foregroundOffset.fY)); SkRect srcRect; src.getBounds(&srcRect); if (NULL != xferEffect) { GrPaint paint; paint.addColorTextureEffect(foregroundTex, foregroundMatrix); paint.addColorEffect(xferEffect)->unref(); context->drawRect(paint, srcRect); } else { GrPaint backgroundPaint; SkMatrix backgroundMatrix = GrEffect::MakeDivByTextureWHMatrix(backgroundTex); backgroundPaint.addColorTextureEffect(backgroundTex, backgroundMatrix); context->drawRect(backgroundPaint, srcRect); GrPaint foregroundPaint; foregroundPaint.setBlendFunc(sk_blend_to_grblend(sm), sk_blend_to_grblend(dm)); foregroundPaint.addColorTextureEffect(foregroundTex, foregroundMatrix); context->drawRect(foregroundPaint, srcRect); } offset->fX += backgroundOffset.fX; offset->fY += backgroundOffset.fY; return SkImageFilterUtils::WrapTexture(dst, src.width(), src.height(), result); }