void FEColorMatrix::apply(Filter* filter) { m_in->apply(filter); if (!m_in->resultImage()) return; GraphicsContext* filterContext = getEffectContext(); if (!filterContext) return; filterContext->drawImage(m_in->resultImage()->image(), calculateDrawingRect(m_in->subRegion())); IntRect imageRect(IntPoint(), resultImage()->size()); PassRefPtr<ImageData> imageData(resultImage()->getUnmultipliedImageData(imageRect)); PassRefPtr<CanvasPixelArray> srcPixelArray(imageData->data()); switch (m_type) { case FECOLORMATRIX_TYPE_UNKNOWN: break; case FECOLORMATRIX_TYPE_MATRIX: effectType<FECOLORMATRIX_TYPE_MATRIX>(srcPixelArray, imageData, m_values); break; case FECOLORMATRIX_TYPE_SATURATE: effectType<FECOLORMATRIX_TYPE_SATURATE>(srcPixelArray, imageData, m_values); break; case FECOLORMATRIX_TYPE_HUEROTATE: effectType<FECOLORMATRIX_TYPE_HUEROTATE>(srcPixelArray, imageData, m_values); break; case FECOLORMATRIX_TYPE_LUMINANCETOALPHA: effectType<FECOLORMATRIX_TYPE_LUMINANCETOALPHA>(srcPixelArray, imageData, m_values); break; } resultImage()->putUnmultipliedImageData(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 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 SVGResourceMasker::applyMask(GraphicsContext* context, const FloatRect& boundingBox) { if (!m_mask) m_mask = m_ownerElement->drawMaskerContent(boundingBox, m_maskRect); if (!m_mask) return; IntSize imageSize(m_mask->size()); IntRect intImageRect(0, 0, imageSize.width(), imageSize.height()); // Create new ImageBuffer to apply luminance OwnPtr<ImageBuffer> luminancedImage = ImageBuffer::create(imageSize); if (!luminancedImage) return; PassRefPtr<CanvasPixelArray> srcPixelArray(m_mask->getImageData(intImageRect)->data()); PassRefPtr<ImageData> destImageData(luminancedImage->getImageData(intImageRect)); for (unsigned pixelOffset = 0; pixelOffset < srcPixelArray->length(); pixelOffset++) { unsigned pixelByteOffset = pixelOffset * 4; unsigned char r = 0, g = 0, b = 0, a = 0; srcPixelArray->get(pixelByteOffset, r); srcPixelArray->get(pixelByteOffset + 1, g); srcPixelArray->get(pixelByteOffset + 2, b); srcPixelArray->get(pixelByteOffset + 3, a); double luma = (r * 0.2125 + g * 0.7154 + b * 0.0721) * ((double)a / 255.0); destImageData->data()->set(pixelByteOffset + 3, luma); } luminancedImage->putImageData(destImageData.get(), intImageRect, IntPoint(0, 0)); context->clipToImageBuffer(m_maskRect, luminancedImage.get()); }
void RenderSVGResourceMasker::createMaskImage(MaskerData* maskerData, const SVGMaskElement* maskElement, RenderObject* object) { FloatRect objectBoundingBox = object->objectBoundingBox(); // Mask rect clipped with clippingBoundingBox and filterBoundingBox as long as they are present. maskerData->maskRect = object->repaintRectInLocalCoordinates(); if (maskerData->maskRect.isEmpty()) { maskerData->emptyMask = true; return; } if (m_maskBoundaries.isEmpty()) calculateMaskContentRepaintRect(); FloatRect repaintRect = m_maskBoundaries; AffineTransform contextTransform; // We need to scale repaintRect for objectBoundingBox to get the drawing area. if (maskElement->maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { contextTransform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height()); FloatPoint contextAdjustment = repaintRect.location(); repaintRect = contextTransform.mapRect(repaintRect); repaintRect.move(objectBoundingBox.x(), objectBoundingBox.y()); contextTransform.translate(-contextAdjustment.x(), -contextAdjustment.y()); } repaintRect.intersect(maskerData->maskRect); maskerData->maskRect = repaintRect; IntRect maskImageRect = enclosingIntRect(maskerData->maskRect); maskImageRect.setLocation(IntPoint()); // Don't create ImageBuffers with image size of 0 if (maskImageRect.isEmpty()) { maskerData->emptyMask = true; return; } // FIXME: This changes color space to linearRGB, the default color space // for masking operations in SVG. We need a switch for the other color-space // attribute values sRGB, inherit and auto. maskerData->maskImage = ImageBuffer::create(maskImageRect.size(), LinearRGB); if (!maskerData->maskImage) return; GraphicsContext* maskImageContext = maskerData->maskImage->context(); ASSERT(maskImageContext); maskImageContext->save(); if (maskElement->maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) maskImageContext->translate(-maskerData->maskRect.x(), -maskerData->maskRect.y()); maskImageContext->concatCTM(contextTransform); // draw the content into the ImageBuffer for (Node* node = maskElement->firstChild(); node; node = node->nextSibling()) { RenderObject* renderer = node->renderer(); if (!node->isSVGElement() || !static_cast<SVGElement*>(node)->isStyled() || !renderer) continue; RenderStyle* style = renderer->style(); if (!style || style->display() == NONE || style->visibility() != VISIBLE) continue; renderSubtreeToImage(maskerData->maskImage.get(), renderer); } maskImageContext->restore(); // create the luminance mask RefPtr<ImageData> imageData(maskerData->maskImage->getUnmultipliedImageData(maskImageRect)); CanvasPixelArray* srcPixelArray(imageData->data()); for (unsigned pixelOffset = 0; pixelOffset < srcPixelArray->length(); pixelOffset += 4) { unsigned char a = srcPixelArray->get(pixelOffset + 3); if (!a) continue; unsigned char r = srcPixelArray->get(pixelOffset); unsigned char g = srcPixelArray->get(pixelOffset + 1); unsigned char b = srcPixelArray->get(pixelOffset + 2); double luma = (r * 0.2125 + g * 0.7154 + b * 0.0721) * ((double)a / 255.0); srcPixelArray->set(pixelOffset + 3, luma); } maskerData->maskImage->putUnmultipliedImageData(imageData.get(), maskImageRect, 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()); }