void FEConvolveMatrix::apply() { if (hasResult()) return; FilterEffect* in = inputEffect(0); in->apply(); if (!in->hasResult()) return; ByteArray* resultImage; if (m_preserveAlpha) resultImage = createUnmultipliedImageResult(); else resultImage = createPremultipliedImageResult(); if (!resultImage) return; IntRect effectDrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect()); RefPtr<ByteArray> srcPixelArray; if (m_preserveAlpha) srcPixelArray = in->asUnmultipliedImage(effectDrawingRect); else srcPixelArray = in->asPremultipliedImage(effectDrawingRect); IntSize paintSize = absolutePaintRect().size(); PaintingData paintingData; paintingData.srcPixelArray = srcPixelArray.get(); paintingData.dstPixelArray = resultImage; paintingData.width = paintSize.width(); paintingData.height = paintSize.height(); paintingData.bias = m_bias * 255; // Drawing fully covered pixels int clipRight = paintSize.width() - m_kernelSize.width(); int clipBottom = paintSize.height() - m_kernelSize.height(); if (clipRight >= 0 && clipBottom >= 0) { #if ENABLE(PARALLEL_JOBS) int optimalThreadNumber = (absolutePaintRect().width() * absolutePaintRect().height()) / s_minimalRectDimension; if (optimalThreadNumber > 1) { ParallelJobs<InteriorPixelParameters> parallelJobs(&WebCore::FEConvolveMatrix::setInteriorPixelsWorker, optimalThreadNumber); const int numOfThreads = parallelJobs.numberOfJobs(); const int heightPerThread = clipBottom / numOfThreads; int startY = 0; for (int job = 0; job < numOfThreads; ++job) { InteriorPixelParameters& param = parallelJobs.parameter(job); param.filter = this; param.paintingData = &paintingData; param.clipRight = clipRight; param.clipBottom = clipBottom; param.yStart = startY; if (job < numOfThreads - 1) { startY += heightPerThread; param.yEnd = startY - 1; } else param.yEnd = clipBottom; } parallelJobs.execute(); } else // Fallback to the default setInteriorPixels call. #endif setInteriorPixels(paintingData, clipRight, clipBottom, 0, clipBottom); clipRight += m_targetOffset.x() + 1; clipBottom += m_targetOffset.y() + 1; if (m_targetOffset.y() > 0) setOuterPixels(paintingData, 0, 0, paintSize.width(), m_targetOffset.y()); if (clipBottom < paintSize.height()) setOuterPixels(paintingData, 0, clipBottom, paintSize.width(), paintSize.height()); if (m_targetOffset.x() > 0) setOuterPixels(paintingData, 0, m_targetOffset.y(), m_targetOffset.x(), clipBottom); if (clipRight < paintSize.width()) setOuterPixels(paintingData, clipRight, m_targetOffset.y(), paintSize.width(), clipBottom); } else { // Rare situation, not optimizied for speed setOuterPixels(paintingData, 0, 0, paintSize.width(), paintSize.height()); } }
void FEConvolveMatrix::platformApplySoftware() { FilterEffect* in = inputEffect(0); Uint8ClampedArray* resultImage; if (m_preserveAlpha) resultImage = createUnmultipliedImageResult(); else resultImage = createPremultipliedImageResult(); if (!resultImage) return; IntRect effectDrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect()); RefPtr<Uint8ClampedArray> srcPixelArray; if (m_preserveAlpha) srcPixelArray = in->asUnmultipliedImage(effectDrawingRect); else srcPixelArray = in->asPremultipliedImage(effectDrawingRect); IntSize paintSize = absolutePaintRect().size(); PaintingData paintingData; paintingData.srcPixelArray = srcPixelArray.get(); paintingData.dstPixelArray = resultImage; paintingData.width = paintSize.width(); paintingData.height = paintSize.height(); paintingData.bias = m_bias * 255; // Drawing fully covered pixels int clipRight = paintSize.width() - m_kernelSize.width(); int clipBottom = paintSize.height() - m_kernelSize.height(); if (clipRight >= 0 && clipBottom >= 0) { int optimalThreadNumber = (absolutePaintRect().width() * absolutePaintRect().height()) / s_minimalRectDimension; if (optimalThreadNumber > 1) { WTF::ParallelJobs<InteriorPixelParameters> parallelJobs(&WebCore::FEConvolveMatrix::setInteriorPixelsWorker, optimalThreadNumber); const int numOfThreads = parallelJobs.numberOfJobs(); // Split the job into "heightPerThread" jobs but there a few jobs that need to be slightly larger since // heightPerThread * jobs < total size. These extras are handled by the remainder "jobsWithExtra". const int heightPerThread = clipBottom / numOfThreads; const int jobsWithExtra = clipBottom % numOfThreads; int startY = 0; for (int job = 0; job < numOfThreads; ++job) { InteriorPixelParameters& param = parallelJobs.parameter(job); param.filter = this; param.paintingData = &paintingData; param.clipRight = clipRight; param.clipBottom = clipBottom; param.yStart = startY; startY += job < jobsWithExtra ? heightPerThread + 1 : heightPerThread; param.yEnd = startY; } parallelJobs.execute(); } else { // Fallback to single threaded mode. setInteriorPixels(paintingData, clipRight, clipBottom, 0, clipBottom); } clipRight += m_targetOffset.x() + 1; clipBottom += m_targetOffset.y() + 1; if (m_targetOffset.y() > 0) setOuterPixels(paintingData, 0, 0, paintSize.width(), m_targetOffset.y()); if (clipBottom < paintSize.height()) setOuterPixels(paintingData, 0, clipBottom, paintSize.width(), paintSize.height()); if (m_targetOffset.x() > 0) setOuterPixels(paintingData, 0, m_targetOffset.y(), m_targetOffset.x(), clipBottom); if (clipRight < paintSize.width()) setOuterPixels(paintingData, clipRight, m_targetOffset.y(), paintSize.width(), clipBottom); } else { // Rare situation, not optimizied for speed setOuterPixels(paintingData, 0, 0, paintSize.width(), paintSize.height()); } }
void FEConvolveMatrix::platformApplySoftware() { FilterEffect* in = inputEffect(0); Uint8ClampedArray* resultImage; if (m_preserveAlpha) resultImage = createUnmultipliedImageResult(); else resultImage = createPremultipliedImageResult(); if (!resultImage) return; IntRect effectDrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect()); RefPtr<Uint8ClampedArray> srcPixelArray; if (m_preserveAlpha) srcPixelArray = in->asUnmultipliedImage(effectDrawingRect); else srcPixelArray = in->asPremultipliedImage(effectDrawingRect); IntSize paintSize = absolutePaintRect().size(); PaintingData paintingData; paintingData.srcPixelArray = srcPixelArray.get(); paintingData.dstPixelArray = resultImage; paintingData.width = paintSize.width(); paintingData.height = paintSize.height(); paintingData.bias = m_bias * 255; // Drawing fully covered pixels int clipRight = paintSize.width() - m_kernelSize.width(); int clipBottom = paintSize.height() - m_kernelSize.height(); if (clipRight < 0 || clipBottom < 0) { // Rare situation, not optimizied for speed setOuterPixels(paintingData, 0, 0, paintSize.width(), paintSize.height()); return; } int iterations = (absolutePaintRect().width() * absolutePaintRect().height()) / s_minimalRectDimension; if (!iterations) { setInteriorPixels(paintingData, clipRight, clipBottom, 0, clipBottom); return; } int stride = clipBottom / iterations; int chunkCount = (clipBottom + stride - 1) / stride; WorkQueue::concurrentApply(chunkCount, [&](size_t index) { int yStart = (stride * index); int yEnd = std::min<int>(stride * (index + 1), clipBottom); setInteriorPixels(paintingData, clipRight, clipBottom, yStart, yEnd); }); clipRight += m_targetOffset.x() + 1; clipBottom += m_targetOffset.y() + 1; if (m_targetOffset.y() > 0) setOuterPixels(paintingData, 0, 0, paintSize.width(), m_targetOffset.y()); if (clipBottom < paintSize.height()) setOuterPixels(paintingData, 0, clipBottom, paintSize.width(), paintSize.height()); if (m_targetOffset.x() > 0) setOuterPixels(paintingData, 0, m_targetOffset.y(), m_targetOffset.x(), clipBottom); if (clipRight < paintSize.width()) setOuterPixels(paintingData, clipRight, m_targetOffset.y(), paintSize.width(), clipBottom); }