sk_sp<SkSpecialImage> SkMorphologyImageFilter::onFilterImage(SkSpecialImage* source, const Context& ctx, SkIPoint* offset) const { SkIPoint inputOffset = SkIPoint::Make(0, 0); sk_sp<SkSpecialImage> input(this->filterInput(0, source, ctx, &inputOffset)); if (!input) { return nullptr; } SkIRect bounds; input = this->applyCropRect(this->mapContext(ctx), input.get(), &inputOffset, &bounds); if (!input) { return nullptr; } SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()), SkIntToScalar(this->radius().height())); ctx.ctm().mapVectors(&radius, 1); int width = SkScalarFloorToInt(radius.fX); int height = SkScalarFloorToInt(radius.fY); if (width < 0 || height < 0) { return nullptr; } SkIRect srcBounds = bounds; srcBounds.offset(-inputOffset); if (0 == width && 0 == height) { offset->fX = bounds.left(); offset->fY = bounds.top(); return input->makeSubset(srcBounds); } #if SK_SUPPORT_GPU if (source->isTextureBacked()) { GrContext* context = source->getContext(); auto type = (kDilate_Op == this->op()) ? GrMorphologyEffect::kDilate_MorphologyType : GrMorphologyEffect::kErode_MorphologyType; sk_sp<SkSpecialImage> result(apply_morphology(context, input.get(), srcBounds, type, SkISize::Make(width, height))); if (result) { offset->fX = bounds.left(); offset->fY = bounds.top(); } return result; } #endif SkBitmap inputBM; if (!input->getROPixels(&inputBM)) { return nullptr; } if (inputBM.colorType() != kN32_SkColorType) { return nullptr; } SkImageInfo info = SkImageInfo::Make(bounds.width(), bounds.height(), inputBM.colorType(), inputBM.alphaType()); SkBitmap dst; if (!dst.tryAllocPixels(info)) { return nullptr; } SkAutoLockPixels inputLock(inputBM), dstLock(dst); SkMorphologyImageFilter::Proc procX, procY; if (kDilate_Op == this->op()) { procX = SkOpts::dilate_x; procY = SkOpts::dilate_y; } else { procX = SkOpts::erode_x; procY = SkOpts::erode_y; } if (width > 0 && height > 0) { SkBitmap tmp; if (!tmp.tryAllocPixels(info)) { return nullptr; } SkAutoLockPixels tmpLock(tmp); call_proc_X(procX, inputBM, &tmp, width, srcBounds); SkIRect tmpBounds = SkIRect::MakeWH(srcBounds.width(), srcBounds.height()); call_proc_Y(procY, tmp.getAddr32(tmpBounds.left(), tmpBounds.top()), tmp.rowBytesAsPixels(), &dst, height, tmpBounds); } else if (width > 0) { call_proc_X(procX, inputBM, &dst, width, srcBounds); } else if (height > 0) { call_proc_Y(procY, inputBM.getAddr32(srcBounds.left(), srcBounds.top()), inputBM.rowBytesAsPixels(), &dst, height, srcBounds); } offset->fX = bounds.left(); offset->fY = bounds.top(); return SkSpecialImage::MakeFromRaster(source->internal_getProxy(), SkIRect::MakeWH(bounds.width(), bounds.height()), dst, &source->props()); }
sk_sp<SkSpecialImage> SkMorphologyImageFilter::onFilterImage(SkSpecialImage* source, const Context& ctx, SkIPoint* offset) const { SkIPoint inputOffset = SkIPoint::Make(0, 0); sk_sp<SkSpecialImage> input(this->filterInput(0, source, ctx, &inputOffset)); if (!input) { return nullptr; } SkIRect bounds; input = this->applyCropRectAndPad(this->mapContext(ctx), input.get(), &inputOffset, &bounds); if (!input) { return nullptr; } SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()), SkIntToScalar(this->radius().height())); ctx.ctm().mapVectors(&radius, 1); int width = SkScalarFloorToInt(radius.fX); int height = SkScalarFloorToInt(radius.fY); if (width < 0 || height < 0) { return nullptr; } SkIRect srcBounds = bounds; srcBounds.offset(-inputOffset); if (0 == width && 0 == height) { offset->fX = bounds.left(); offset->fY = bounds.top(); return input->makeSubset(srcBounds); } #if SK_SUPPORT_GPU if (source->isTextureBacked()) { GrContext* context = source->getContext(); // Ensure the input is in the destination color space. Typically applyCropRect will have // called pad_image to account for our dilation of bounds, so the result will already be // moved to the destination color space. If a filter DAG avoids that, then we use this // fall-back, which saves us from having to do the xform during the filter itself. input = ImageToColorSpace(input.get(), ctx.outputProperties()); auto type = (kDilate_Op == this->op()) ? GrMorphologyEffect::Type::kDilate : GrMorphologyEffect::Type::kErode; sk_sp<SkSpecialImage> result(apply_morphology(context, input.get(), srcBounds, type, SkISize::Make(width, height), ctx.outputProperties())); if (result) { offset->fX = bounds.left(); offset->fY = bounds.top(); } return result; } #endif SkBitmap inputBM; if (!input->getROPixels(&inputBM)) { return nullptr; } if (inputBM.colorType() != kN32_SkColorType) { return nullptr; } SkImageInfo info = SkImageInfo::Make(bounds.width(), bounds.height(), inputBM.colorType(), inputBM.alphaType()); SkBitmap dst; if (!dst.tryAllocPixels(info)) { return nullptr; } SkMorphologyImageFilter::Proc procX, procY; if (kDilate_Op == this->op()) { procX = SkOpts::dilate_x; procY = SkOpts::dilate_y; } else { procX = SkOpts::erode_x; procY = SkOpts::erode_y; } if (width > 0 && height > 0) { SkBitmap tmp; if (!tmp.tryAllocPixels(info)) { return nullptr; } call_proc_X(procX, inputBM, &tmp, width, srcBounds); SkIRect tmpBounds = SkIRect::MakeWH(srcBounds.width(), srcBounds.height()); call_proc_Y(procY, tmp.getAddr32(tmpBounds.left(), tmpBounds.top()), tmp.rowBytesAsPixels(), &dst, height, tmpBounds); } else if (width > 0) { call_proc_X(procX, inputBM, &dst, width, srcBounds); } else if (height > 0) { call_proc_Y(procY, inputBM.getAddr32(srcBounds.left(), srcBounds.top()), inputBM.rowBytesAsPixels(), &dst, height, srcBounds); } offset->fX = bounds.left(); offset->fY = bounds.top(); return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(bounds.width(), bounds.height()), dst, &source->props()); }