void FEComposite::apply(Filter* filter) { m_in->apply(filter); m_in2->apply(filter); if (!m_in->resultImage() || !m_in2->resultImage()) return; GraphicsContext* filterContext = getEffectContext(); if (!filterContext) return; FloatRect srcRect = FloatRect(0.f, 0.f, -1.f, -1.f); switch (m_type) { case FECOMPOSITE_OPERATOR_OVER: filterContext->drawImageBuffer(m_in2->resultImage(), DeviceColorSpace, calculateDrawingRect(m_in2->scaledSubRegion())); filterContext->drawImageBuffer(m_in->resultImage(), DeviceColorSpace, calculateDrawingRect(m_in->scaledSubRegion())); break; case FECOMPOSITE_OPERATOR_IN: filterContext->save(); filterContext->clipToImageBuffer(m_in2->resultImage(), calculateDrawingRect(m_in2->scaledSubRegion())); filterContext->drawImageBuffer(m_in->resultImage(), DeviceColorSpace, calculateDrawingRect(m_in->scaledSubRegion())); filterContext->restore(); break; case FECOMPOSITE_OPERATOR_OUT: filterContext->drawImageBuffer(m_in->resultImage(), DeviceColorSpace, calculateDrawingRect(m_in->scaledSubRegion())); filterContext->drawImageBuffer(m_in2->resultImage(), DeviceColorSpace, calculateDrawingRect(m_in2->scaledSubRegion()), srcRect, CompositeDestinationOut); break; case FECOMPOSITE_OPERATOR_ATOP: filterContext->drawImageBuffer(m_in2->resultImage(), DeviceColorSpace, calculateDrawingRect(m_in2->scaledSubRegion())); filterContext->drawImageBuffer(m_in->resultImage(), DeviceColorSpace, calculateDrawingRect(m_in->scaledSubRegion()), srcRect, CompositeSourceAtop); break; case FECOMPOSITE_OPERATOR_XOR: filterContext->drawImageBuffer(m_in2->resultImage(), DeviceColorSpace, calculateDrawingRect(m_in2->scaledSubRegion())); filterContext->drawImageBuffer(m_in->resultImage(), DeviceColorSpace, calculateDrawingRect(m_in->scaledSubRegion()), srcRect, CompositeXOR); break; case FECOMPOSITE_OPERATOR_ARITHMETIC: { IntRect effectADrawingRect = calculateDrawingIntRect(m_in->scaledSubRegion()); RefPtr<CanvasPixelArray> srcPixelArrayA(m_in->resultImage()->getPremultipliedImageData(effectADrawingRect)->data()); IntRect effectBDrawingRect = calculateDrawingIntRect(m_in2->scaledSubRegion()); RefPtr<ImageData> imageData(m_in2->resultImage()->getPremultipliedImageData(effectBDrawingRect)); CanvasPixelArray* srcPixelArrayB(imageData->data()); arithmetic(srcPixelArrayA, srcPixelArrayB, m_k1, m_k2, m_k3, m_k4); resultImage()->putPremultipliedImageData(imageData.get(), IntRect(IntPoint(), resultImage()->size()), IntPoint()); } break; default: break; } }
void FEDisplacementMap::apply(Filter* filter) { m_in->apply(filter); m_in2->apply(filter); if (!m_in->resultImage() || !m_in2->resultImage()) return; if (m_xChannelSelector == CHANNEL_UNKNOWN || m_yChannelSelector == CHANNEL_UNKNOWN) return; if (!getEffectContext()) return; IntRect effectADrawingRect = calculateDrawingIntRect(m_in->scaledSubRegion()); RefPtr<CanvasPixelArray> srcPixelArrayA(m_in->resultImage()->getPremultipliedImageData(effectADrawingRect)->data()); IntRect effectBDrawingRect = calculateDrawingIntRect(m_in2->scaledSubRegion()); RefPtr<CanvasPixelArray> srcPixelArrayB(m_in2->resultImage()->getUnmultipliedImageData(effectBDrawingRect)->data()); IntRect imageRect(IntPoint(), resultImage()->size()); RefPtr<ImageData> imageData = ImageData::create(imageRect.width(), imageRect.height()); ASSERT(srcPixelArrayA->length() == srcPixelArrayB->length()); float scaleX = m_scale / 255.f * filter->filterResolution().width(); float scaleY = m_scale / 255.f * filter->filterResolution().height(); float scaleAdjustmentX = (0.5f - 0.5f * m_scale) * filter->filterResolution().width(); float scaleAdjustmentY = (0.5f - 0.5f * m_scale) * filter->filterResolution().height(); int stride = imageRect.width() * 4; for (int y = 0; y < imageRect.height(); ++y) { int line = y * stride; for (int x = 0; x < imageRect.width(); ++x) { int dstIndex = line + x * 4; int srcX = x + static_cast<int>(scaleX * srcPixelArrayB->get(dstIndex + m_xChannelSelector - 1) + scaleAdjustmentX); int srcY = y + static_cast<int>(scaleY * srcPixelArrayB->get(dstIndex + m_yChannelSelector - 1) + scaleAdjustmentY); for (unsigned channel = 0; channel < 4; ++channel) { if (srcX < 0 || srcX >= imageRect.width() || srcY < 0 || srcY >= imageRect.height()) imageData->data()->set(dstIndex + channel, static_cast<unsigned char>(0)); else { unsigned char pixelValue = srcPixelArrayA->get(srcY * stride + srcX * 4 + channel); imageData->data()->set(dstIndex + channel, pixelValue); } } } } resultImage()->putPremultipliedImageData(imageData.get(), imageRect, IntPoint()); }
void FEGaussianBlur::apply(Filter* filter) { m_in->apply(filter); if (!m_in->resultImage()) return; if (!getEffectContext()) return; setIsAlphaImage(m_in->isAlphaImage()); if (m_x == 0 || m_y == 0) return; unsigned sdx = static_cast<unsigned>(floor(m_x * 3 * sqrt(2 * M_PI) / 4.f + 0.5f)); unsigned sdy = static_cast<unsigned>(floor(m_y * 3 * sqrt(2 * M_PI) / 4.f + 0.5f)); IntRect effectDrawingRect = calculateDrawingIntRect(m_in->subRegion()); RefPtr<ImageData> srcImageData(m_in->resultImage()->getPremultipliedImageData(effectDrawingRect)); CanvasPixelArray* srcPixelArray(srcImageData->data()); IntRect imageRect(IntPoint(), resultImage()->size()); RefPtr<ImageData> tmpImageData = ImageData::create(imageRect.width(), imageRect.height()); CanvasPixelArray* tmpPixelArray(tmpImageData->data()); int stride = 4 * imageRect.width(); for (int i = 0; i < 3; ++i) { boxBlur(srcPixelArray, tmpPixelArray, sdx, 4, stride, imageRect.width(), imageRect.height(), isAlphaImage()); boxBlur(tmpPixelArray, srcPixelArray, sdy, stride, 4, imageRect.height(), imageRect.width(), isAlphaImage()); } resultImage()->putPremultipliedImageData(srcImageData.get(), imageRect, IntPoint()); }
void FEBlend::apply(Filter* filter) { m_in->apply(filter); m_in2->apply(filter); if (!m_in->resultImage() || !m_in2->resultImage()) return; if (m_mode == FEBLEND_MODE_UNKNOWN) return; if (!getEffectContext()) return; IntRect effectADrawingRect = calculateDrawingIntRect(m_in->scaledSubRegion()); RefPtr<CanvasPixelArray> srcPixelArrayA(m_in->resultImage()->getPremultipliedImageData(effectADrawingRect)->data()); IntRect effectBDrawingRect = calculateDrawingIntRect(m_in2->scaledSubRegion()); RefPtr<CanvasPixelArray> srcPixelArrayB(m_in2->resultImage()->getPremultipliedImageData(effectBDrawingRect)->data()); IntRect imageRect(IntPoint(), resultImage()->size()); RefPtr<ImageData> imageData = ImageData::create(imageRect.width(), imageRect.height()); // Keep synchronized with BlendModeType static const BlendType callEffect[] = {unknown, normal, multiply, screen, darken, lighten}; ASSERT(srcPixelArrayA->length() == srcPixelArrayB->length()); for (unsigned pixelOffset = 0; pixelOffset < srcPixelArrayA->length(); pixelOffset += 4) { unsigned char alphaA = srcPixelArrayA->get(pixelOffset + 3); unsigned char alphaB = srcPixelArrayB->get(pixelOffset + 3); for (unsigned channel = 0; channel < 3; ++channel) { unsigned char colorA = srcPixelArrayA->get(pixelOffset + channel); unsigned char colorB = srcPixelArrayB->get(pixelOffset + channel); unsigned char result = (*callEffect[m_mode])(colorA, colorB, alphaA, alphaB); imageData->data()->set(pixelOffset + channel, result); } unsigned char alphaR = 255 - ((255 - alphaA) * (255 - alphaB)) / 255; imageData->data()->set(pixelOffset + 3, alphaR); } resultImage()->putPremultipliedImageData(imageData.get(), imageRect, IntPoint()); }
void FELighting::apply(Filter* filter) { m_in->apply(filter); if (!m_in->resultImage()) return; if (!getEffectContext()) return; setIsAlphaImage(false); IntRect effectDrawingRect = calculateDrawingIntRect(m_in->scaledSubRegion()); RefPtr<ImageData> srcImageData(m_in->resultImage()->getUnmultipliedImageData(effectDrawingRect)); CanvasPixelArray* srcPixelArray(srcImageData->data()); // FIXME: support kernelUnitLengths other than (1,1). The issue here is that the W3 // standard has no test case for them, and other browsers (like Firefox) has strange // output for various kernelUnitLengths, and I am not sure they are reliable. // Anyway, feConvolveMatrix should also use the implementation if (drawLighting(srcPixelArray, effectDrawingRect.width(), effectDrawingRect.height())) resultImage()->putUnmultipliedImageData(srcImageData.get(), IntRect(IntPoint(), resultImage()->size()), IntPoint()); }
void FEMorphology::apply(Filter* filter) { m_in->apply(filter); if (!m_in->resultImage()) return; if (!getEffectContext()) return; setIsAlphaImage(m_in->isAlphaImage()); int radiusX = static_cast<int>(m_radiusX * filter->filterResolution().width()); int radiusY = static_cast<int>(m_radiusY * filter->filterResolution().height()); if (radiusX <= 0 || radiusY <= 0) return; IntRect imageRect(IntPoint(), resultImage()->size()); IntRect effectDrawingRect = calculateDrawingIntRect(m_in->scaledSubRegion()); RefPtr<CanvasPixelArray> srcPixelArray(m_in->resultImage()->getPremultipliedImageData(effectDrawingRect)->data()); RefPtr<ImageData> imageData = ImageData::create(imageRect.width(), imageRect.height()); int effectWidth = effectDrawingRect.width() * 4; // Limit the radius size to effect dimensions radiusX = min(effectDrawingRect.width() - 1, radiusX); radiusY = min(effectDrawingRect.height() - 1, radiusY); Vector<unsigned char> extrema; for (int y = 0; y < effectDrawingRect.height(); ++y) { int startY = max(0, y - radiusY); int endY = min(effectDrawingRect.height() - 1, y + radiusY); for (unsigned channel = 0; channel < 4; ++channel) { // Fill the kernel extrema.clear(); for (int j = 0; j <= radiusX; ++j) { unsigned char columnExtrema = srcPixelArray->get(startY * effectWidth + 4 * j + channel); for (int i = startY; i <= endY; ++i) { unsigned char pixel = srcPixelArray->get(i * effectWidth + 4 * j + channel); if ((m_type == FEMORPHOLOGY_OPERATOR_ERODE && pixel <= columnExtrema) || (m_type == FEMORPHOLOGY_OPERATOR_DILATE && pixel >= columnExtrema)) columnExtrema = pixel; } extrema.append(columnExtrema); } // Kernel is filled, get extrema of next column for (int x = 0; x < effectDrawingRect.width(); ++x) { unsigned endX = min(x + radiusX, effectDrawingRect.width() - 1); unsigned char columnExtrema = srcPixelArray->get(startY * effectWidth + endX * 4 + channel); for (int i = startY; i <= endY; ++i) { unsigned char pixel = srcPixelArray->get(i * effectWidth + endX * 4 + channel); if ((m_type == FEMORPHOLOGY_OPERATOR_ERODE && pixel <= columnExtrema) || (m_type == FEMORPHOLOGY_OPERATOR_DILATE && pixel >= columnExtrema)) columnExtrema = pixel; } if (x - radiusX >= 0) extrema.remove(0); if (x + radiusX <= effectDrawingRect.width()) extrema.append(columnExtrema); unsigned char entireExtrema = extrema[0]; for (unsigned kernelIndex = 0; kernelIndex < extrema.size(); ++kernelIndex) { if ((m_type == FEMORPHOLOGY_OPERATOR_ERODE && extrema[kernelIndex] <= entireExtrema) || (m_type == FEMORPHOLOGY_OPERATOR_DILATE && extrema[kernelIndex] >= entireExtrema)) entireExtrema = extrema[kernelIndex]; } imageData->data()->set(y * effectWidth + 4 * x + channel, entireExtrema); } } } resultImage()->putPremultipliedImageData(imageData.get(), imageRect, IntPoint()); }