SkImageFilter* CanvasRenderingContext2DState::getFilter(Element* styleResolutionHost, const Font& font) const { if (!m_filterValue) return nullptr; if (!m_resolvedFilter) { RefPtr<ComputedStyle> filterStyle = ComputedStyle::create(); // Must set font in case the filter uses any font-relative units (em, ex) filterStyle->setFont(font); StyleResolverState resolverState(styleResolutionHost->document(), styleResolutionHost, filterStyle.get()); resolverState.setStyle(filterStyle); // TODO(junov): crbug.com/502877 Feed m_fillStyle and m_strokeStyle into FillPaint and // StrokePaint respectively for filters that reference SVG. StyleBuilder::applyProperty(CSSPropertyWebkitFilter, resolverState, m_filterValue.get()); RefPtrWillBeRawPtr<FilterEffectBuilder> filterEffectBuilder = FilterEffectBuilder::create(); const float effectiveZoom = 1.0f; // Deliberately ignore zoom on the canvas element filterEffectBuilder->build(styleResolutionHost, filterStyle->filter(), effectiveZoom); SkiaImageFilterBuilder imageFilterBuilder; RefPtrWillBeRawPtr<FilterEffect> lastEffect = filterEffectBuilder->lastEffect(); m_resolvedFilter = imageFilterBuilder.build(lastEffect.get(), ColorSpaceDeviceRGB); } return m_resolvedFilter.get(); }
SkImageFilter* CanvasRenderingContext2DState::getFilter(Element* styleResolutionHost, const Font& font, IntSize canvasSize) const { if (!m_filterValue) return nullptr; if (!m_resolvedFilter) { RefPtr<ComputedStyle> filterStyle = ComputedStyle::create(); // Must set font in case the filter uses any font-relative units (em, ex) filterStyle->setFont(font); StyleResolverState resolverState(styleResolutionHost->document(), styleResolutionHost, filterStyle.get()); resolverState.setStyle(filterStyle); StyleBuilder::applyProperty(CSSPropertyWebkitFilter, resolverState, m_filterValue.get()); RefPtrWillBeRawPtr<FilterEffectBuilder> filterEffectBuilder = FilterEffectBuilder::create(); // We can't reuse m_fillPaint and m_strokePaint for the filter, since these incorporate // the global alpha, which isn't applicable here. SkPaint fillPaintForFilter; SkPaint strokePaintForFilter; m_fillStyle->applyToPaint(fillPaintForFilter); m_strokeStyle->applyToPaint(strokePaintForFilter); fillPaintForFilter.setColor(m_fillStyle->paintColor()); strokePaintForFilter.setColor(m_strokeStyle->paintColor()); FloatSize floatCanvasSize(canvasSize); const double effectiveZoom = 1.0; // Deliberately ignore zoom on the canvas element filterEffectBuilder->build(styleResolutionHost, filterStyle->filter(), effectiveZoom, &floatCanvasSize, &fillPaintForFilter, &strokePaintForFilter); SkiaImageFilterBuilder imageFilterBuilder; RefPtrWillBeRawPtr<FilterEffect> lastEffect = filterEffectBuilder->lastEffect(); m_resolvedFilter = imageFilterBuilder.build(lastEffect.get(), ColorSpaceDeviceRGB); } return m_resolvedFilter.get(); }
PassRefPtr<SkImageFilter> FEBlend::createImageFilter(SkiaImageFilterBuilder& builder) { RefPtr<SkImageFilter> foreground(builder.build(inputEffect(0), operatingColorSpace())); RefPtr<SkImageFilter> background(builder.build(inputEffect(1), operatingColorSpace())); sk_sp<SkXfermode> mode(SkXfermode::Make(WebCoreCompositeToSkiaComposite(CompositeSourceOver, m_mode))); SkImageFilter::CropRect cropRect = getCropRect(); return fromSkSp(SkXfermodeImageFilter::Make(std::move(mode), background.get(), foreground.get(), &cropRect)); }
FilterPainter::FilterPainter(DeprecatedPaintLayer& layer, GraphicsContext* context, const LayoutPoint& offsetFromRoot, const ClipRect& clipRect, DeprecatedPaintLayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags, LayoutRect& rootRelativeBounds, bool& rootRelativeBoundsComputed) : m_filterInProgress(false) , m_context(context) , m_layoutObject(layer.layoutObject()) { if (!layer.filterEffectBuilder() || !layer.paintsWithFilters()) return; ASSERT(layer.filterInfo()); SkiaImageFilterBuilder builder; RefPtrWillBeRawPtr<FilterEffect> lastEffect = layer.filterEffectBuilder()->lastEffect(); lastEffect->determineFilterPrimitiveSubregion(MapRectForward); RefPtr<SkImageFilter> imageFilter = builder.build(lastEffect.get(), ColorSpaceDeviceRGB); if (!imageFilter) return; if (!rootRelativeBoundsComputed) { rootRelativeBounds = layer.physicalBoundingBoxIncludingReflectionAndStackingChildren(paintingInfo.rootLayer, offsetFromRoot); rootRelativeBoundsComputed = true; } // We'll handle clipping to the dirty rect before filter rasterization. // Filter processing will automatically expand the clip rect and the offscreen to accommodate any filter outsets. // FIXME: It is incorrect to just clip to the damageRect here once multiple fragments are involved. // Subsequent code should not clip to the dirty rect, since we've already // done it above, and doing it later will defeat the outsets. paintingInfo.clipToDirtyRect = false; if (clipRect.rect() != paintingInfo.paintDirtyRect || clipRect.hasRadius()) { m_clipRecorder = adoptPtr(new LayerClipRecorder(*context, *layer.layoutObject(), DisplayItem::ClipLayerFilter, clipRect, &paintingInfo, LayoutPoint(), paintFlags)); } ASSERT(m_layoutObject); if (RuntimeEnabledFeatures::slimmingPaintEnabled()) { ASSERT(context->displayItemList()); if (!context->displayItemList()->displayItemConstructionIsDisabled()) { FilterOperations filterOperations(layer.computeFilterOperations(m_layoutObject->styleRef())); OwnPtr<WebFilterOperations> webFilterOperations = adoptPtr(Platform::current()->compositorSupport()->createFilterOperations()); builder.buildFilterOperations(filterOperations, webFilterOperations.get()); // FIXME: It's possible to have empty WebFilterOperations here even // though the SkImageFilter produced above is non-null, since the // layer's FilterEffectBuilder can have a stale representation of // the layer's filter. See crbug.com/502026. if (webFilterOperations->isEmpty()) return; context->displayItemList()->createAndAppend<BeginFilterDisplayItem>(*m_layoutObject, imageFilter, rootRelativeBounds, webFilterOperations.release()); } } else { BeginFilterDisplayItem filterDisplayItem(*m_layoutObject, imageFilter, rootRelativeBounds); filterDisplayItem.replay(*context); } m_filterInProgress = true; }
PassRefPtr<SkImageFilter> FEMorphology::createImageFilter(SkiaImageFilterBuilder& builder) { RefPtr<SkImageFilter> input(builder.build(inputEffect(0), operatingColorSpace())); SkScalar radiusX = SkFloatToScalar(filter()->applyHorizontalScale(m_radiusX)); SkScalar radiusY = SkFloatToScalar(filter()->applyVerticalScale(m_radiusY)); SkImageFilter::CropRect rect = getCropRect(); if (m_type == FEMORPHOLOGY_OPERATOR_DILATE) return adoptRef(SkDilateImageFilter::Create(radiusX, radiusY, input.get(), &rect)); return adoptRef(SkErodeImageFilter::Create(radiusX, radiusY, input.get(), &rect)); }
PassRefPtr<SkImageFilter> SourceAlpha::createImageFilter(SkiaImageFilterBuilder& builder) { RefPtr<SkImageFilter> sourceGraphic(builder.build(inputEffect(0), operatingColorSpace())); SkScalar matrix[20] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, SK_Scalar1, 0 }; RefPtr<SkColorFilter> colorFilter(adoptRef(SkColorMatrixFilter::Create(matrix))); return adoptRef(SkColorFilterImageFilter::Create(colorFilter.get(), sourceGraphic.get())); }
PassRefPtr<SkImageFilter> FEComponentTransfer::createImageFilter(SkiaImageFilterBuilder& builder) { RefPtr<SkImageFilter> input(builder.build(inputEffect(0), operatingColorSpace())); unsigned char rValues[256], gValues[256], bValues[256], aValues[256]; getValues(rValues, gValues, bValues, aValues); SkAutoTUnref<SkColorFilter> colorFilter(SkTableColorFilter::CreateARGB(aValues, rValues, gValues, bValues)); SkImageFilter::CropRect cropRect = getCropRect(builder.cropOffset()); return adoptRef(SkColorFilterImageFilter::Create(colorFilter, input.get(), &cropRect)); }
PassRefPtr<SkImageFilter> FEMerge::createImageFilter(SkiaImageFilterBuilder& builder) { unsigned size = numberOfEffectInputs(); OwnPtr<RefPtr<SkImageFilter>[]> inputRefs = adoptArrayPtr(new RefPtr<SkImageFilter>[size]); OwnPtr<SkImageFilter*[]> inputs = adoptArrayPtr(new SkImageFilter*[size]); for (unsigned i = 0; i < size; ++i) { inputRefs[i] = builder.build(inputEffect(i), operatingColorSpace()); inputs[i] = inputRefs[i].get(); } SkImageFilter::CropRect rect = getCropRect(builder.cropOffset()); return adoptRef(SkMergeImageFilter::Create(inputs.get(), size, 0, &rect)); }
PassRefPtr<SkImageFilter> FELighting::createImageFilter(SkiaImageFilterBuilder& builder) { if (!m_lightSource) return createTransparentBlack(builder); SkImageFilter::CropRect rect = getCropRect(builder.cropOffset()); Color lightColor = adaptColorToOperatingColorSpace(m_lightingColor); RefPtr<SkImageFilter> input(builder.build(inputEffect(0), operatingColorSpace())); switch (m_lightSource->type()) { case LS_DISTANT: { DistantLightSource* distantLightSource = static_cast<DistantLightSource*>(m_lightSource.get()); float azimuthRad = deg2rad(distantLightSource->azimuth()); float elevationRad = deg2rad(distantLightSource->elevation()); const SkPoint3 direction = SkPoint3::Make(cosf(azimuthRad) * cosf(elevationRad), sinf(azimuthRad) * cosf(elevationRad), sinf(elevationRad)); if (m_specularConstant > 0) return adoptRef(SkLightingImageFilter::CreateDistantLitSpecular(direction, lightColor.rgb(), m_surfaceScale, m_specularConstant, m_specularExponent, input.get(), &rect)); return adoptRef(SkLightingImageFilter::CreateDistantLitDiffuse(direction, lightColor.rgb(), m_surfaceScale, m_diffuseConstant, input.get(), &rect)); } case LS_POINT: { PointLightSource* pointLightSource = static_cast<PointLightSource*>(m_lightSource.get()); const FloatPoint3D position = pointLightSource->position(); const SkPoint3 skPosition = SkPoint3::Make(position.x(), position.y(), position.z()); if (m_specularConstant > 0) return adoptRef(SkLightingImageFilter::CreatePointLitSpecular(skPosition, lightColor.rgb(), m_surfaceScale, m_specularConstant, m_specularExponent, input.get(), &rect)); return adoptRef(SkLightingImageFilter::CreatePointLitDiffuse(skPosition, lightColor.rgb(), m_surfaceScale, m_diffuseConstant, input.get(), &rect)); } case LS_SPOT: { SpotLightSource* spotLightSource = static_cast<SpotLightSource*>(m_lightSource.get()); const SkPoint3 location = SkPoint3::Make(spotLightSource->position().x(), spotLightSource->position().y(), spotLightSource->position().z()); const SkPoint3 target = SkPoint3::Make(spotLightSource->direction().x(), spotLightSource->direction().y(), spotLightSource->direction().z()); float specularExponent = spotLightSource->specularExponent(); float limitingConeAngle = spotLightSource->limitingConeAngle(); if (!limitingConeAngle || limitingConeAngle > 90 || limitingConeAngle < -90) limitingConeAngle = 90; if (m_specularConstant > 0) return adoptRef(SkLightingImageFilter::CreateSpotLitSpecular(location, target, specularExponent, limitingConeAngle, lightColor.rgb(), m_surfaceScale, m_specularConstant, m_specularExponent, input.get(), &rect)); return adoptRef(SkLightingImageFilter::CreateSpotLitDiffuse(location, target, specularExponent, limitingConeAngle, lightColor.rgb(), m_surfaceScale, m_diffuseConstant, input.get(), &rect)); } default: ASSERT_NOT_REACHED(); return nullptr; } }
PassRefPtr<SkImageFilter> FEConvolveMatrix::createImageFilter(SkiaImageFilterBuilder& builder) { if (!parametersValid()) return createTransparentBlack(builder); RefPtr<SkImageFilter> input(builder.build(inputEffect(0), operatingColorSpace())); SkISize kernelSize(SkISize::Make(m_kernelSize.width(), m_kernelSize.height())); // parametersValid() above checks that the kernel area fits in int. int numElements = safeCast<int>(m_kernelSize.area()); SkScalar gain = SkFloatToScalar(1.0f / m_divisor); SkScalar bias = SkFloatToScalar(m_bias * 255); SkIPoint target = SkIPoint::Make(m_targetOffset.x(), m_targetOffset.y()); SkMatrixConvolutionImageFilter::TileMode tileMode = toSkiaTileMode(m_edgeMode); bool convolveAlpha = !m_preserveAlpha; OwnPtr<SkScalar[]> kernel = adoptArrayPtr(new SkScalar[numElements]); for (int i = 0; i < numElements; ++i) kernel[i] = SkFloatToScalar(m_kernelMatrix[numElements - 1 - i]); SkImageFilter::CropRect cropRect = getCropRect(builder.cropOffset()); return adoptRef(SkMatrixConvolutionImageFilter::Create(kernelSize, kernel.get(), gain, bias, target, tileMode, convolveAlpha, input.get(), &cropRect)); }
static void paintFilteredContent(const LayoutObject& object, GraphicsContext& context, FilterData* filterData) { ASSERT(filterData->m_state == FilterData::ReadyToPaint); ASSERT(filterData->filter->sourceGraphic()); filterData->m_state = FilterData::PaintingFilter; SkiaImageFilterBuilder builder; RefPtr<SkImageFilter> imageFilter = builder.build(filterData->filter->lastEffect(), ColorSpaceDeviceRGB); FloatRect boundaries = filterData->filter->filterRegion(); context.save(); // Clip drawing of filtered image to the minimum required paint rect. FilterEffect* lastEffect = filterData->filter->lastEffect(); context.clipRect(lastEffect->determineAbsolutePaintRect(lastEffect->maxEffectRect())); #ifdef CHECK_CTM_FOR_TRANSFORMED_IMAGEFILTER // TODO: Remove this workaround once skew/rotation support is added in Skia // (https://code.google.com/p/skia/issues/detail?id=3288, crbug.com/446935). // If the CTM contains rotation or shearing, apply the filter to // the unsheared/unrotated matrix, and do the shearing/rotation // as a final pass. AffineTransform ctm = SVGLayoutSupport::deprecatedCalculateTransformToLayer(&object); if (ctm.b() || ctm.c()) { AffineTransform scaleAndTranslate; scaleAndTranslate.translate(ctm.e(), ctm.f()); scaleAndTranslate.scale(ctm.xScale(), ctm.yScale()); ASSERT(scaleAndTranslate.isInvertible()); AffineTransform shearAndRotate = scaleAndTranslate.inverse(); shearAndRotate.multiply(ctm); context.concatCTM(shearAndRotate.inverse()); imageFilter = builder.buildTransform(shearAndRotate, imageFilter.get()); } #endif context.beginLayer(1, SkXfermode::kSrcOver_Mode, &boundaries, ColorFilterNone, imageFilter.get()); context.endLayer(); context.restore(); filterData->m_state = FilterData::ReadyToPaint; }
PassRefPtr<SkImageFilter> FEBoxReflect::createImageFilter(SkiaImageFilterBuilder& builder) { RefPtr<SkImageFilter> input(builder.build(inputEffect(0), operatingColorSpace())); return builder.buildBoxReflectFilter(m_reflectionDirection, m_offset, nullptr, input.get()); }
void colorSpaceTest() { // Build filter tree RefPtr<ReferenceFilter> referenceFilter = ReferenceFilter::create(); // Add a dummy source graphic input RefPtr<FilterEffect> sourceEffect = referenceFilter->sourceGraphic(); sourceEffect->setOperatingColorSpace(ColorSpaceDeviceRGB); // Add a blur effect (with input : source) RefPtr<FilterEffect> blurEffect = FEGaussianBlur::create(referenceFilter.get(), 3.0f, 3.0f); blurEffect->setOperatingColorSpace(ColorSpaceLinearRGB); blurEffect->inputEffects().append(sourceEffect); // Add a blend effect (with inputs : blur, source) RefPtr<FilterEffect> blendEffect = FEBlend::create(referenceFilter.get(), FEBLEND_MODE_NORMAL); blendEffect->setOperatingColorSpace(ColorSpaceDeviceRGB); FilterEffectVector& blendInputs = blendEffect->inputEffects(); blendInputs.reserveCapacity(2); blendInputs.append(sourceEffect); blendInputs.append(blurEffect); // Add a merge effect (with inputs : blur, blend) RefPtr<FilterEffect> mergeEffect = FEMerge::create(referenceFilter.get()); mergeEffect->setOperatingColorSpace(ColorSpaceLinearRGB); FilterEffectVector& mergeInputs = mergeEffect->inputEffects(); mergeInputs.reserveCapacity(2); mergeInputs.append(blurEffect); mergeInputs.append(blendEffect); referenceFilter->setLastEffect(mergeEffect); // Get SkImageFilter resulting tree SkiaImageFilterBuilder builder; RefPtr<SkImageFilter> filter = builder.build(referenceFilter->lastEffect(), ColorSpaceDeviceRGB); // Let's check that the resulting tree looks like this : // ColorSpace (Linear->Device) : CS (L->D) // | // Merge (L) // | | // | CS (D->L) // | | // | Blend (D) // | / | // | CS (L->D) | // | / | // Blur (L) | // \ | // CS (D->L) | // \ | // Source Graphic (D) EXPECT_EQ(filter->countInputs(), 1); // Should be CS (L->D) SkImageFilter* child = filter->getInput(0); // Should be Merge EXPECT_EQ(child->asColorFilter(0), false); EXPECT_EQ(child->countInputs(), 2); child = child->getInput(1); // Should be CS (D->L) EXPECT_EQ(child->asColorFilter(0), true); EXPECT_EQ(child->countInputs(), 1); child = child->getInput(0); // Should be Blend EXPECT_EQ(child->asColorFilter(0), false); EXPECT_EQ(child->countInputs(), 2); child = child->getInput(0); // Should be CS (L->D) EXPECT_EQ(child->asColorFilter(0), true); EXPECT_EQ(child->countInputs(), 1); child = child->getInput(0); // Should be Blur EXPECT_EQ(child->asColorFilter(0), false); EXPECT_EQ(child->countInputs(), 1); child = child->getInput(0); // Should be CS (D->L) EXPECT_EQ(child->asColorFilter(0), true); EXPECT_EQ(child->countInputs(), 1); }