Changeset 73894 in webkit
- Timestamp:
- Dec 13, 2010 2:15:53 AM (13 years ago)
- Location:
- trunk/WebCore
- Files:
-
- 1 added
- 23 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/WebCore/ChangeLog
r73892 r73894 1 2010-12-13 Zoltan Herczeg <zherczeg@webkit.org> 2 3 Reviewed by Dirk Schulze. 4 5 Better result passing in filter primitives 6 https://bugs.webkit.org/show_bug.cgi?id=49907 7 8 SVG filter primitives can use the output of other filters. The 9 input and output format of a filter can be a premultiplied or 10 unmultiplied RGBA array, or an image buffer. All filter 11 primitive results were converted to image buffers before, which 12 could be an unecessary (and really costly) operation, if 13 a filter expects its input in the same format as the output 14 of the input filter primitive. This overhead is removed by 15 this patch. All apply() methods are updated according to this 16 new filter primitive interface. 17 18 Filters do not generate their results twice (or more) anymore, 19 when their apply() called multiple times. 20 21 The existing tests cover this feature. 22 23 * manual-tests/svg-filter-animation.svg: Added. 24 * platform/graphics/filters/FEBlend.cpp: 25 (WebCore::FEBlend::apply): 26 * platform/graphics/filters/FEColorMatrix.cpp: 27 (WebCore::FEColorMatrix::apply): 28 * platform/graphics/filters/FEComponentTransfer.cpp: 29 (WebCore::FEComponentTransfer::apply): 30 * platform/graphics/filters/FEComposite.cpp: 31 (WebCore::FEComposite::apply): 32 * platform/graphics/filters/FEConvolveMatrix.cpp: 33 (WebCore::FEConvolveMatrix::apply): 34 * platform/graphics/filters/FEDisplacementMap.cpp: 35 (WebCore::FEDisplacementMap::apply): 36 * platform/graphics/filters/FEFlood.cpp: 37 (WebCore::FEFlood::apply): 38 * platform/graphics/filters/FEGaussianBlur.cpp: 39 (WebCore::FEGaussianBlur::apply): 40 * platform/graphics/filters/FELighting.cpp: 41 (WebCore::FELighting::apply): 42 * platform/graphics/filters/FEMerge.cpp: 43 (WebCore::FEMerge::apply): 44 * platform/graphics/filters/FEMerge.h: 45 * platform/graphics/filters/FEMorphology.cpp: 46 (WebCore::FEMorphology::apply): 47 * platform/graphics/filters/FEOffset.cpp: 48 (WebCore::FEOffset::apply): 49 * platform/graphics/filters/FETile.cpp: 50 (WebCore::FETile::apply): 51 * platform/graphics/filters/FETurbulence.cpp: 52 (WebCore::FETurbulence::apply): 53 * platform/graphics/filters/FilterEffect.cpp: 54 (WebCore::FilterEffect::requestedRegionOfInputImageData): 55 (WebCore::FilterEffect::asImageBuffer): 56 (WebCore::FilterEffect::asUnmultipliedImage): 57 (WebCore::FilterEffect::asPremultipliedImage): 58 (WebCore::FilterEffect::copyImageBytes): 59 (WebCore::FilterEffect::copyUnmultipliedImage): 60 (WebCore::FilterEffect::copyPremultipliedImage): 61 (WebCore::FilterEffect::createImageBufferResult): 62 (WebCore::FilterEffect::createUnmultipliedImageResult): 63 (WebCore::FilterEffect::createPremultipliedImageResult): 64 * platform/graphics/filters/FilterEffect.h: 65 (WebCore::FilterEffect::hasResult): 66 * platform/graphics/filters/SourceAlpha.cpp: 67 (WebCore::SourceAlpha::apply): 68 * platform/graphics/filters/SourceGraphic.cpp: 69 (WebCore::SourceGraphic::apply): 70 * platform/graphics/filters/SourceGraphic.h: 71 * rendering/RenderSVGResourceFilter.cpp: 72 (WebCore::RenderSVGResourceFilter::postApplyResource): 73 * svg/graphics/filters/SVGFEImage.cpp: 74 (WebCore::FEImage::apply): 75 1 76 2010-12-13 Csaba Osztrogonác <ossy@webkit.org> 2 77 -
trunk/WebCore/platform/graphics/filters/FEBlend.cpp
r72474 r73894 89 89 void FEBlend::apply() 90 90 { 91 if (hasResult()) 92 return; 91 93 FilterEffect* in = inputEffect(0); 92 94 FilterEffect* in2 = inputEffect(1); 93 95 in->apply(); 94 96 in2->apply(); 95 if (!in-> resultImage() || !in2->resultImage())97 if (!in->hasResult() || !in2->hasResult()) 96 98 return; 97 99 … … 99 101 return; 100 102 101 if (!effectContext()) 103 ImageData* resultImage = createPremultipliedImageResult(); 104 if (!resultImage) 102 105 return; 103 106 104 107 IntRect effectADrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect()); 105 RefPtr<ImageData> srcImageDataA = in-> resultImage()->getPremultipliedImageData(effectADrawingRect);108 RefPtr<ImageData> srcImageDataA = in->asPremultipliedImage(effectADrawingRect); 106 109 ByteArray* srcPixelArrayA = srcImageDataA->data()->data(); 107 110 108 111 IntRect effectBDrawingRect = requestedRegionOfInputImageData(in2->absolutePaintRect()); 109 RefPtr<ImageData> srcImageDataB = in2-> resultImage()->getPremultipliedImageData(effectBDrawingRect);112 RefPtr<ImageData> srcImageDataB = in2->asPremultipliedImage(effectBDrawingRect); 110 113 ByteArray* srcPixelArrayB = srcImageDataB->data()->data(); 111 114 112 IntRect imageRect(IntPoint(), resultImage()->size()); 113 RefPtr<ImageData> imageData = ImageData::create(imageRect.width(), imageRect.height()); 114 ByteArray* dstPixelArray = imageData->data()->data(); 115 ByteArray* dstPixelArray = resultImage->data()->data(); 115 116 116 117 // Keep synchronized with BlendModeType … … 132 133 dstPixelArray->set(pixelOffset + 3, alphaR); 133 134 } 134 135 resultImage()->putPremultipliedImageData(imageData.get(), imageRect, IntPoint());136 135 } 137 136 -
trunk/WebCore/platform/graphics/filters/FEColorMatrix.cpp
r72474 r73894 151 151 void FEColorMatrix::apply() 152 152 { 153 if (hasResult()) 154 return; 153 155 FilterEffect* in = inputEffect(0); 154 156 in->apply(); 155 if (!in-> resultImage())157 if (!in->hasResult()) 156 158 return; 157 159 158 GraphicsContext* filterContext = effectContext();159 if (! filterContext)160 ImageBuffer* resultImage = createImageBufferResult(); 161 if (!resultImage) 160 162 return; 161 163 162 filterContext->drawImageBuffer(in->resultImage(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect()));163 164 IntRect imageRect(IntPoint(), resultImage()->size());165 RefPtr<ImageData> imageData = resultImage ()->getUnmultipliedImageData(imageRect);164 resultImage->context()->drawImageBuffer(in->asImageBuffer(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect())); 165 166 IntRect imageRect(IntPoint(), absolutePaintRect().size()); 167 RefPtr<ImageData> imageData = resultImage->getUnmultipliedImageData(imageRect); 166 168 ByteArray* pixelArray = imageData->data()->data(); 167 169 … … 184 186 } 185 187 186 resultImage ()->putUnmultipliedImageData(imageData.get(), imageRect, IntPoint());188 resultImage->putUnmultipliedImageData(imageData.get(), imageRect, IntPoint()); 187 189 } 188 190 -
trunk/WebCore/platform/graphics/filters/FEComponentTransfer.cpp
r72474 r73894 150 150 void FEComponentTransfer::apply() 151 151 { 152 if (hasResult()) 153 return; 152 154 FilterEffect* in = inputEffect(0); 153 155 in->apply(); 154 if (!in->resultImage()) 155 return; 156 157 if (!effectContext()) 156 if (!in->hasResult()) 157 return; 158 159 ImageData* imageData = createUnmultipliedImageResult(); 160 if (!imageData) 158 161 return; 159 162 … … 169 172 170 173 IntRect drawingRect = requestedRegionOfInputImageData(in->absolutePaintRect()); 171 RefPtr<ImageData> imageData = in->resultImage()->getUnmultipliedImageData(drawingRect);174 in->copyUnmultipliedImage(imageData, drawingRect); 172 175 ByteArray* pixelArray = imageData->data()->data(); 173 176 … … 179 182 } 180 183 } 181 182 resultImage()->putUnmultipliedImageData(imageData.get(), IntRect(IntPoint(), resultImage()->size()), IntPoint());183 184 } 184 185 -
trunk/WebCore/platform/graphics/filters/FEComposite.cpp
r72474 r73894 138 138 void FEComposite::apply() 139 139 { 140 if (hasResult()) 141 return; 140 142 FilterEffect* in = inputEffect(0); 141 143 FilterEffect* in2 = inputEffect(1); 142 144 in->apply(); 143 145 in2->apply(); 144 if (!in->resultImage() || !in2->resultImage()) 145 return; 146 147 GraphicsContext* filterContext = effectContext(); 148 if (!filterContext) 149 return; 146 if (!in->hasResult() || !in2->hasResult()) 147 return; 148 149 ImageBuffer* resultImage = createImageBufferResult(); 150 if (!resultImage) 151 return; 152 GraphicsContext* filterContext = resultImage->context(); 150 153 151 154 FloatRect srcRect = FloatRect(0, 0, -1, -1); 152 155 switch (m_type) { 153 156 case FECOMPOSITE_OPERATOR_OVER: 154 filterContext->drawImageBuffer(in2-> resultImage(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in2->absolutePaintRect()));155 filterContext->drawImageBuffer(in-> resultImage(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect()));157 filterContext->drawImageBuffer(in2->asImageBuffer(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in2->absolutePaintRect())); 158 filterContext->drawImageBuffer(in->asImageBuffer(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect())); 156 159 break; 157 160 case FECOMPOSITE_OPERATOR_IN: 158 161 filterContext->save(); 159 filterContext->clipToImageBuffer(in2-> resultImage(), drawingRegionOfInputImage(in2->absolutePaintRect()));160 filterContext->drawImageBuffer(in-> resultImage(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect()));162 filterContext->clipToImageBuffer(in2->asImageBuffer(), drawingRegionOfInputImage(in2->absolutePaintRect())); 163 filterContext->drawImageBuffer(in->asImageBuffer(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect())); 161 164 filterContext->restore(); 162 165 break; 163 166 case FECOMPOSITE_OPERATOR_OUT: 164 filterContext->drawImageBuffer(in-> resultImage(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect()));165 filterContext->drawImageBuffer(in2-> resultImage(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in2->absolutePaintRect()), srcRect, CompositeDestinationOut);167 filterContext->drawImageBuffer(in->asImageBuffer(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect())); 168 filterContext->drawImageBuffer(in2->asImageBuffer(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in2->absolutePaintRect()), srcRect, CompositeDestinationOut); 166 169 break; 167 170 case FECOMPOSITE_OPERATOR_ATOP: 168 filterContext->drawImageBuffer(in2-> resultImage(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in2->absolutePaintRect()));169 filterContext->drawImageBuffer(in-> resultImage(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect()), srcRect, CompositeSourceAtop);171 filterContext->drawImageBuffer(in2->asImageBuffer(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in2->absolutePaintRect())); 172 filterContext->drawImageBuffer(in->asImageBuffer(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect()), srcRect, CompositeSourceAtop); 170 173 break; 171 174 case FECOMPOSITE_OPERATOR_XOR: 172 filterContext->drawImageBuffer(in2-> resultImage(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in2->absolutePaintRect()));173 filterContext->drawImageBuffer(in-> resultImage(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect()), srcRect, CompositeXOR);175 filterContext->drawImageBuffer(in2->asImageBuffer(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in2->absolutePaintRect())); 176 filterContext->drawImageBuffer(in->asImageBuffer(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect()), srcRect, CompositeXOR); 174 177 break; 175 178 case FECOMPOSITE_OPERATOR_ARITHMETIC: { 176 179 IntRect effectADrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect()); 177 RefPtr<ImageData> srcImageData = in-> resultImage()->getPremultipliedImageData(effectADrawingRect);180 RefPtr<ImageData> srcImageData = in->asImageBuffer()->getPremultipliedImageData(effectADrawingRect); 178 181 ByteArray* srcPixelArrayA = srcImageData->data()->data(); 179 182 180 183 IntRect effectBDrawingRect = requestedRegionOfInputImageData(in2->absolutePaintRect()); 181 RefPtr<ImageData> imageData = in2-> resultImage()->getPremultipliedImageData(effectBDrawingRect);184 RefPtr<ImageData> imageData = in2->asImageBuffer()->getPremultipliedImageData(effectBDrawingRect); 182 185 ByteArray* srcPixelArrayB = imageData->data()->data(); 183 186 184 187 arithmetic(srcPixelArrayA, srcPixelArrayB, m_k1, m_k2, m_k3, m_k4); 185 resultImage ()->putPremultipliedImageData(imageData.get(), IntRect(IntPoint(), resultImage()->size()), IntPoint());188 resultImage->putPremultipliedImageData(imageData.get(), IntRect(IntPoint(), resultImage->size()), IntPoint()); 186 189 } 187 190 break; -
trunk/WebCore/platform/graphics/filters/FEConvolveMatrix.cpp
r72474 r73894 373 373 void FEConvolveMatrix::apply() 374 374 { 375 if (hasResult()) 376 return; 375 377 FilterEffect* in = inputEffect(0); 376 378 in->apply(); 377 if (!in-> resultImage())379 if (!in->hasResult()) 378 380 return; 379 381 380 if (!effectContext()) 382 ImageData* resultImage; 383 if (m_preserveAlpha) 384 resultImage = createUnmultipliedImageResult(); 385 else 386 resultImage = createPremultipliedImageResult(); 387 if (!resultImage) 381 388 return; 382 389 383 IntRect imageRect(IntPoint(), resultImage()->size());384 390 IntRect effectDrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect()); 385 391 386 392 RefPtr<CanvasPixelArray> srcPixelArray; 387 393 if (m_preserveAlpha) 388 srcPixelArray = in-> resultImage()->getUnmultipliedImageData(effectDrawingRect)->data();394 srcPixelArray = in->asUnmultipliedImage(effectDrawingRect)->data(); 389 395 else 390 srcPixelArray = in->resultImage()->getPremultipliedImageData(effectDrawingRect)->data(); 391 392 RefPtr<ImageData> imageData = ImageData::create(imageRect.width(), imageRect.height()); 393 396 srcPixelArray = in->asPremultipliedImage(effectDrawingRect)->data(); 397 398 IntSize paintSize = absolutePaintRect().size(); 394 399 PaintingData paintingData; 395 400 paintingData.srcPixelArray = srcPixelArray.get(); 396 paintingData.dstPixelArray = imageData->data();397 paintingData.width = imageRect.width();398 paintingData.height = imageRect.height();401 paintingData.dstPixelArray = resultImage->data(); 402 paintingData.width = paintSize.width(); 403 paintingData.height = paintSize.height(); 399 404 paintingData.bias = m_bias * 255; 400 405 401 406 // Drawing fully covered pixels 402 int clipRight = imageRect.width() - m_kernelSize.width();403 int clipBottom = imageRect.height() - m_kernelSize.height();407 int clipRight = paintSize.width() - m_kernelSize.width(); 408 int clipBottom = paintSize.height() - m_kernelSize.height(); 404 409 405 410 if (clipRight >= 0 && clipBottom >= 0) { … … 409 414 clipBottom += m_targetOffset.y() + 1; 410 415 if (m_targetOffset.y() > 0) 411 setOuterPixels(paintingData, 0, 0, imageRect.width(), m_targetOffset.y());412 if (clipBottom < imageRect.height())413 setOuterPixels(paintingData, 0, clipBottom, imageRect.width(), imageRect.height());416 setOuterPixels(paintingData, 0, 0, paintSize.width(), m_targetOffset.y()); 417 if (clipBottom < paintSize.height()) 418 setOuterPixels(paintingData, 0, clipBottom, paintSize.width(), paintSize.height()); 414 419 if (m_targetOffset.x() > 0) 415 420 setOuterPixels(paintingData, 0, m_targetOffset.y(), m_targetOffset.x(), clipBottom); 416 if (clipRight < imageRect.width())417 setOuterPixels(paintingData, clipRight, m_targetOffset.y(), imageRect.width(), clipBottom);421 if (clipRight < paintSize.width()) 422 setOuterPixels(paintingData, clipRight, m_targetOffset.y(), paintSize.width(), clipBottom); 418 423 } else { 419 424 // Rare situation, not optimizied for speed 420 setOuterPixels(paintingData, 0, 0, imageRect.width(), imageRect.height());425 setOuterPixels(paintingData, 0, 0, paintSize.width(), paintSize.height()); 421 426 } 422 423 if (m_preserveAlpha)424 resultImage()->putUnmultipliedImageData(imageData.get(), imageRect, IntPoint());425 else426 resultImage()->putPremultipliedImageData(imageData.get(), imageRect, IntPoint());427 427 } 428 428 -
trunk/WebCore/platform/graphics/filters/FEDisplacementMap.cpp
r72474 r73894 79 79 void FEDisplacementMap::apply() 80 80 { 81 if (hasResult()) 82 return; 81 83 FilterEffect* in = inputEffect(0); 82 84 FilterEffect* in2 = inputEffect(1); 83 85 in->apply(); 84 86 in2->apply(); 85 if (!in-> resultImage() || !in2->resultImage())87 if (!in->hasResult() || !in2->hasResult()) 86 88 return; 87 89 … … 89 91 return; 90 92 91 if (!effectContext()) 93 ImageData* resultImage = createPremultipliedImageResult(); 94 if (!resultImage) 92 95 return; 93 96 94 97 IntRect effectADrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect()); 95 RefPtr<ImageData> srcImageDataA = in-> resultImage()->getPremultipliedImageData(effectADrawingRect);96 ByteArray* srcPixelArrayA = srcImageDataA->data()->data() 98 RefPtr<ImageData> srcImageDataA = in->asPremultipliedImage(effectADrawingRect); 99 ByteArray* srcPixelArrayA = srcImageDataA->data()->data(); 97 100 98 101 IntRect effectBDrawingRect = requestedRegionOfInputImageData(in2->absolutePaintRect()); 99 RefPtr<ImageData> srcImageDataB = in2-> resultImage()->getUnmultipliedImageData(effectBDrawingRect);102 RefPtr<ImageData> srcImageDataB = in2->asUnmultipliedImage(effectBDrawingRect); 100 103 ByteArray* srcPixelArrayB = srcImageDataB->data()->data(); 101 104 102 IntRect imageRect(IntPoint(), resultImage()->size()); 103 RefPtr<ImageData> imageData = ImageData::create(imageRect.width(), imageRect.height()); 104 ByteArray* dstPixelArray = imageData->data()->data(); 105 ByteArray* dstPixelArray = resultImage->data()->data(); 105 106 106 107 ASSERT(srcPixelArrayA->length() == srcPixelArrayB->length()); 107 108 108 109 Filter* filter = this->filter(); 110 IntSize paintSize = absolutePaintRect().size(); 109 111 float scaleX = filter->applyHorizontalScale(m_scale / 255); 110 112 float scaleY = filter->applyVerticalScale(m_scale / 255); 111 113 float scaleAdjustmentX = filter->applyHorizontalScale(0.5f - 0.5f * m_scale); 112 114 float scaleAdjustmentY = filter->applyVerticalScale(0.5f - 0.5f * m_scale); 113 int stride = imageRect.width() * 4;114 for (int y = 0; y < imageRect.height(); ++y) {115 int stride = paintSize.width() * 4; 116 for (int y = 0; y < paintSize.height(); ++y) { 115 117 int line = y * stride; 116 for (int x = 0; x < imageRect.width(); ++x) {118 for (int x = 0; x < paintSize.width(); ++x) { 117 119 int dstIndex = line + x * 4; 118 120 int srcX = x + static_cast<int>(scaleX * srcPixelArrayB->get(dstIndex + m_xChannelSelector - 1) + scaleAdjustmentX); 119 121 int srcY = y + static_cast<int>(scaleY * srcPixelArrayB->get(dstIndex + m_yChannelSelector - 1) + scaleAdjustmentY); 120 122 for (unsigned channel = 0; channel < 4; ++channel) { 121 if (srcX < 0 || srcX >= imageRect.width() || srcY < 0 || srcY >= imageRect.height())123 if (srcX < 0 || srcX >= paintSize.width() || srcY < 0 || srcY >= paintSize.height()) 122 124 dstPixelArray->set(dstIndex + channel, static_cast<unsigned char>(0)); 123 125 else { … … 126 128 } 127 129 } 128 129 130 } 130 131 } 131 resultImage()->putPremultipliedImageData(imageData.get(), imageRect, IntPoint());132 132 } 133 133 -
trunk/WebCore/platform/graphics/filters/FEFlood.cpp
r72474 r73894 65 65 void FEFlood::apply() 66 66 { 67 GraphicsContext* filterContext = effectContext(); 68 if (!filterContext) 67 if (hasResult()) 68 return; 69 ImageBuffer* resultImage = createImageBufferResult(); 70 if (!resultImage) 69 71 return; 70 72 71 73 Color color = colorWithOverrideAlpha(floodColor().rgb(), floodOpacity()); 72 filterContext->fillRect(FloatRect(FloatPoint(), absolutePaintRect().size()), color, ColorSpaceDeviceRGB);74 resultImage->context()->fillRect(FloatRect(FloatPoint(), absolutePaintRect().size()), color, ColorSpaceDeviceRGB); 73 75 } 74 76 -
trunk/WebCore/platform/graphics/filters/FEGaussianBlur.cpp
r72474 r73894 165 165 void FEGaussianBlur::apply() 166 166 { 167 if (hasResult()) 168 return; 167 169 FilterEffect* in = inputEffect(0); 168 170 in->apply(); 169 if (!in->resultImage()) 170 return; 171 172 if (!effectContext()) 171 if (!in->hasResult()) 172 return; 173 174 ImageData* resultImage = createPremultipliedImageResult(); 175 if (!resultImage) 173 176 return; 174 177 … … 176 179 177 180 IntRect effectDrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect()); 178 RefPtr<ImageData> srcImageData = in->resultImage()->getPremultipliedImageData(effectDrawingRect); 179 IntRect imageRect(IntPoint(), resultImage()->size()); 180 181 if (!m_stdX && !m_stdY) { 182 resultImage()->putPremultipliedImageData(srcImageData.get(), imageRect, IntPoint()); 183 return; 184 } 181 in->copyPremultipliedImage(resultImage, effectDrawingRect); 182 183 if (!m_stdX && !m_stdY) 184 return; 185 185 186 186 unsigned kernelSizeX = 0; … … 188 188 calculateKernelSize(filter(), kernelSizeX, kernelSizeY, m_stdX, m_stdY); 189 189 190 ByteArray* srcPixelArray = srcImageData->data()->data(); 191 RefPtr<ImageData> tmpImageData = ImageData::create(imageRect.width(), imageRect.height()); 190 IntSize paintSize = absolutePaintRect().size(); 191 ByteArray* srcPixelArray = resultImage->data()->data(); 192 RefPtr<ImageData> tmpImageData = ImageData::create(paintSize.width(), paintSize.height()); 192 193 ByteArray* tmpPixelArray = tmpImageData->data()->data(); 193 194 194 int stride = 4 * imageRect.width();195 int stride = 4 * paintSize.width(); 195 196 int dxLeft = 0; 196 197 int dxRight = 0; … … 200 201 if (kernelSizeX) { 201 202 kernelPosition(i, kernelSizeX, dxLeft, dxRight); 202 boxBlur(srcPixelArray, tmpPixelArray, kernelSizeX, dxLeft, dxRight, 4, stride, imageRect.width(), imageRect.height(), isAlphaImage());203 boxBlur(srcPixelArray, tmpPixelArray, kernelSizeX, dxLeft, dxRight, 4, stride, paintSize.width(), paintSize.height(), isAlphaImage()); 203 204 } else { 204 205 ByteArray* auxPixelArray = tmpPixelArray; … … 209 210 if (kernelSizeY) { 210 211 kernelPosition(i, kernelSizeY, dyLeft, dyRight); 211 boxBlur(tmpPixelArray, srcPixelArray, kernelSizeY, dyLeft, dyRight, stride, 4, imageRect.height(), imageRect.width(), isAlphaImage());212 boxBlur(tmpPixelArray, srcPixelArray, kernelSizeY, dyLeft, dyRight, stride, 4, paintSize.height(), paintSize.width(), isAlphaImage()); 212 213 } else { 213 214 ByteArray* auxPixelArray = tmpPixelArray; … … 216 217 } 217 218 } 218 219 resultImage()->putPremultipliedImageData(srcImageData.get(), imageRect, IntPoint());220 219 } 221 220 -
trunk/WebCore/platform/graphics/filters/FELighting.cpp
r72474 r73894 332 332 void FELighting::apply() 333 333 { 334 if (hasResult()) 335 return; 334 336 FilterEffect* in = inputEffect(0); 335 337 in->apply(); 336 if (!in-> resultImage())338 if (!in->hasResult()) 337 339 return; 338 340 339 if (!effectContext()) 341 ImageData* resultImage = createUnmultipliedImageResult(); 342 if (!resultImage) 340 343 return; 341 344 … … 343 346 344 347 IntRect effectDrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect()); 345 RefPtr<ImageData> srcImageData = in->resultImage()->getUnmultipliedImageData(effectDrawingRect);346 ByteArray* srcPixelArray = srcImageData->data()->data();348 in->copyUnmultipliedImage(resultImage, effectDrawingRect); 349 ByteArray* srcPixelArray = resultImage->data()->data(); 347 350 348 351 // FIXME: support kernelUnitLengths other than (1,1). The issue here is that the W3 … … 352 355 353 356 IntSize absolutePaintSize = absolutePaintRect().size(); 354 if (drawLighting(srcPixelArray, absolutePaintSize.width(), absolutePaintSize.height())) 355 resultImage()->putUnmultipliedImageData(srcImageData.get(), IntRect(IntPoint(), absolutePaintSize), IntPoint()); 357 drawLighting(srcPixelArray, absolutePaintSize.width(), absolutePaintSize.height()); 356 358 } 357 359 -
trunk/WebCore/platform/graphics/filters/FEMerge.cpp
r72474 r73894 42 42 void FEMerge::apply() 43 43 { 44 if (hasResult()) 45 return; 44 46 unsigned size = numberOfEffectInputs(); 45 47 ASSERT(size > 0); … … 47 49 FilterEffect* in = inputEffect(i); 48 50 in->apply(); 49 if (!in-> resultImage())51 if (!in->hasResult()) 50 52 return; 51 53 } 52 54 53 GraphicsContext* filterContext = effectContext();54 if (! filterContext)55 ImageBuffer* resultImage = createImageBufferResult(); 56 if (!resultImage) 55 57 return; 56 58 59 GraphicsContext* filterContext = resultImage->context(); 57 60 for (unsigned i = 0; i < size; ++i) { 58 61 FilterEffect* in = inputEffect(i); 59 filterContext->drawImageBuffer(in-> resultImage(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect()));62 filterContext->drawImageBuffer(in->asImageBuffer(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect())); 60 63 } 61 64 } -
trunk/WebCore/platform/graphics/filters/FEMerge.h
r72474 r73894 21 21 22 22 #ifndef FEMerge_h 23 #define EMerge_h23 #define FEMerge_h 24 24 25 25 #if ENABLE(FILTERS) -
trunk/WebCore/platform/graphics/filters/FEMorphology.cpp
r72474 r73894 92 92 void FEMorphology::apply() 93 93 { 94 if (hasResult()) 95 return; 94 96 FilterEffect* in = inputEffect(0); 95 97 in->apply(); 96 if (!in->resultImage()) 97 return; 98 99 if (!effectContext()) 98 if (!in->hasResult()) 99 return; 100 101 ImageData* resultImage = createPremultipliedImageResult(); 102 if (!resultImage) 100 103 return; 101 104 … … 108 111 int radiusY = static_cast<int>(floorf(filter->applyVerticalScale(m_radiusY))); 109 112 110 IntRect imageRect(IntPoint(), resultImage()->size());111 113 IntRect effectDrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect()); 112 RefPtr<ImageData> srcImageData = in-> resultImage()->getPremultipliedImageData(effectDrawingRect);114 RefPtr<ImageData> srcImageData = in->asPremultipliedImage(effectDrawingRect); 113 115 ByteArray* srcPixelArray = srcImageData->data()->data(); 114 RefPtr<ImageData> imageData = ImageData::create(imageRect.width(), imageRect.height()); 115 ByteArray* dstPixelArray = imageData->data()->data(); 116 ByteArray* dstPixelArray = resultImage->data()->data(); 116 117 117 118 int effectWidth = effectDrawingRect.width() * 4; … … 163 164 } 164 165 } 165 resultImage()->putPremultipliedImageData(imageData.get(), imageRect, IntPoint());166 166 } 167 167 -
trunk/WebCore/platform/graphics/filters/FEOffset.cpp
r72474 r73894 75 75 void FEOffset::apply() 76 76 { 77 if (hasResult()) 78 return; 77 79 FilterEffect* in = inputEffect(0); 78 80 in->apply(); 79 if (!in-> resultImage())81 if (!in->hasResult()) 80 82 return; 81 83 82 GraphicsContext* filterContext = effectContext();83 if (! filterContext)84 ImageBuffer* resultImage = createImageBufferResult(); 85 if (!resultImage) 84 86 return; 85 87 … … 89 91 Filter* filter = this->filter(); 90 92 drawingRegion.move(filter->applyHorizontalScale(m_dx), filter->applyVerticalScale(m_dy)); 91 filterContext->drawImageBuffer(in->resultImage(), ColorSpaceDeviceRGB, drawingRegion);93 resultImage->context()->drawImageBuffer(in->asImageBuffer(), ColorSpaceDeviceRGB, drawingRegion); 92 94 } 93 95 -
trunk/WebCore/platform/graphics/filters/FETile.cpp
r72474 r73894 46 46 // FIXME: See bug 47315. This is a hack to work around a compile failure, but is incorrect behavior otherwise. 47 47 #if ENABLE(SVG) 48 if (hasResult()) 49 return; 48 50 FilterEffect* in = inputEffect(0); 49 51 in->apply(); 50 if (!in-> resultImage())52 if (!in->hasResult()) 51 53 return; 52 54 53 GraphicsContext* filterContext = effectContext();54 if (! filterContext)55 ImageBuffer* resultImage = createImageBufferResult(); 56 if (!resultImage) 55 57 return; 56 58 … … 74 76 GraphicsContext* tileImageContext = tileImage->context(); 75 77 tileImageContext->translate(-inMaxEffectLocation.x(), -inMaxEffectLocation.y()); 76 tileImageContext->drawImageBuffer(in-> resultImage(), ColorSpaceDeviceRGB, in->absolutePaintRect().location());78 tileImageContext->drawImageBuffer(in->asImageBuffer(), ColorSpaceDeviceRGB, in->absolutePaintRect().location()); 77 79 78 80 RefPtr<Pattern> pattern = Pattern::create(tileImage->copyImage(), true, true); … … 81 83 patternTransform.translate(inMaxEffectLocation.x() - maxEffectLocation.x(), inMaxEffectLocation.y() - maxEffectLocation.y()); 82 84 pattern->setPatternSpaceTransform(patternTransform); 85 GraphicsContext* filterContext = resultImage->context(); 83 86 filterContext->setFillPattern(pattern); 84 87 filterContext->fillRect(FloatRect(FloatPoint(), absolutePaintRect().size())); -
trunk/WebCore/platform/graphics/filters/FETurbulence.cpp
r72474 r73894 321 321 void FETurbulence::apply() 322 322 { 323 if ( !effectContext())323 if (hasResult()) 324 324 return; 325 326 IntRect imageRect(IntPoint(), resultImage()->size()); 327 if (!imageRect.size().width() || !imageRect.size().height()) 325 ImageData* resultImage = createUnmultipliedImageResult(); 326 if (!resultImage) 328 327 return; 329 328 330 RefPtr<ImageData> imageData = ImageData::create(imageRect.width(), imageRect.height()); 331 ByteArray* pixelArray = imageData->data()->data(); 329 if (absolutePaintRect().isEmpty()) 330 return; 331 332 ByteArray* pixelArray = resultImage->data()->data(); 332 333 PaintingData paintingData(m_seed, roundedIntSize(filterPrimitiveSubregion().size())); 333 334 initPaint(paintingData); … … 337 338 point.setY(filterRegion.y()); 338 339 int indexOfPixelChannel = 0; 339 for (int y = 0; y < imageRect.height(); ++y) {340 for (int y = 0; y < absolutePaintRect().height(); ++y) { 340 341 point.setY(point.y() + 1); 341 342 point.setX(filterRegion.x()); 342 for (int x = 0; x < imageRect.width(); ++x) {343 for (int x = 0; x < absolutePaintRect().width(); ++x) { 343 344 point.setX(point.x() + 1); 344 345 for (paintingData.channel = 0; paintingData.channel < 4; ++paintingData.channel, ++indexOfPixelChannel) … … 346 347 } 347 348 } 348 resultImage()->putUnmultipliedImageData(imageData.get(), imageRect, IntPoint());349 349 } 350 350 -
trunk/WebCore/platform/graphics/filters/FilterEffect.cpp
r72474 r73894 24 24 #if ENABLE(FILTERS) 25 25 #include "FilterEffect.h" 26 #include "ImageData.h" 26 27 27 28 namespace WebCore { … … 55 56 IntRect FilterEffect::requestedRegionOfInputImageData(const IntRect& effectRect) const 56 57 { 57 ASSERT( m_effectBuffer);58 ASSERT(hasResult()); 58 59 IntPoint location = m_absolutePaintRect.location(); 59 60 location.move(-effectRect.x(), -effectRect.y()); 60 return IntRect(location, m_ effectBuffer->size());61 return IntRect(location, m_absolutePaintRect.size()); 61 62 } 62 63 … … 73 74 } 74 75 75 GraphicsContext* FilterEffect::effectContext() 76 { 76 ImageBuffer* FilterEffect::asImageBuffer() 77 { 78 if (!hasResult()) 79 return 0; 80 if (m_imageBufferResult) 81 return m_imageBufferResult.get(); 82 m_imageBufferResult = ImageBuffer::create(m_absolutePaintRect.size(), ColorSpaceLinearRGB); 83 IntRect destinationRect(IntPoint(), m_absolutePaintRect.size()); 84 if (m_premultipliedImageResult) 85 m_imageBufferResult->putPremultipliedImageData(m_premultipliedImageResult.get(), destinationRect, IntPoint()); 86 else 87 m_imageBufferResult->putUnmultipliedImageData(m_unmultipliedImageResult.get(), destinationRect, IntPoint()); 88 return m_imageBufferResult.get(); 89 } 90 91 PassRefPtr<ImageData> FilterEffect::asUnmultipliedImage(const IntRect& rect) 92 { 93 RefPtr<ImageData> imageData = ImageData::create(rect.width(), rect.height()); 94 copyUnmultipliedImage(imageData.get(), rect); 95 return imageData.release(); 96 } 97 98 PassRefPtr<ImageData> FilterEffect::asPremultipliedImage(const IntRect& rect) 99 { 100 RefPtr<ImageData> imageData = ImageData::create(rect.width(), rect.height()); 101 copyPremultipliedImage(imageData.get(), rect); 102 return imageData.release(); 103 } 104 105 inline void FilterEffect::copyImageBytes(ImageData* source, ImageData* destination, const IntRect& rect) 106 { 107 // Copy the necessary lines. 108 ASSERT(IntSize(destination->width(), destination->height()) == rect.size()); 109 if (rect.x() < 0 || rect.y() < 0 || rect.bottom() > m_absolutePaintRect.width() || rect.bottom() > m_absolutePaintRect.height()) 110 memset(destination->data()->data()->data(), 0, destination->data()->length()); 111 112 int xOrigin = rect.x(); 113 int xDest = 0; 114 if (xOrigin < 0) { 115 xDest = -xOrigin; 116 xOrigin = 0; 117 } 118 int xEnd = rect.right(); 119 if (xEnd > m_absolutePaintRect.width()) 120 xEnd = m_absolutePaintRect.width(); 121 122 int yOrigin = rect.y(); 123 int yDest = 0; 124 if (yOrigin < 0) { 125 yDest = -yOrigin; 126 yOrigin = 0; 127 } 128 int yEnd = rect.bottom(); 129 if (yEnd > m_absolutePaintRect.height()) 130 yEnd = m_absolutePaintRect.height(); 131 132 int size = (xEnd - xOrigin) * 4; 133 int destinationScanline = rect.width() * 4; 134 int sourceScanline = m_absolutePaintRect.width() * 4; 135 unsigned char *destinationPixel = destination->data()->data()->data() + ((yDest * rect.width()) + xDest) * 4; 136 unsigned char *sourcePixel = source->data()->data()->data() + ((yOrigin * m_absolutePaintRect.width()) + xOrigin) * 4; 137 138 while (yOrigin < yEnd) { 139 memcpy(destinationPixel, sourcePixel, size); 140 destinationPixel += destinationScanline; 141 sourcePixel += sourceScanline; 142 ++yOrigin; 143 } 144 } 145 146 void FilterEffect::copyUnmultipliedImage(ImageData* destination, const IntRect& rect) 147 { 148 ASSERT(hasResult()); 149 150 if (!m_unmultipliedImageResult) { 151 // We prefer a conversion from the image buffer. 152 if (m_imageBufferResult) 153 m_unmultipliedImageResult = m_imageBufferResult->getUnmultipliedImageData(IntRect(IntPoint(), m_absolutePaintRect.size())); 154 else { 155 m_unmultipliedImageResult = ImageData::create(m_absolutePaintRect.width(), m_absolutePaintRect.height()); 156 unsigned char* sourceComponent = m_premultipliedImageResult->data()->data()->data(); 157 unsigned char* destinationComponent = m_unmultipliedImageResult->data()->data()->data(); 158 unsigned char* end = sourceComponent + (m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4); 159 while (sourceComponent < end) { 160 int alpha = sourceComponent[3]; 161 if (alpha) { 162 destinationComponent[0] = static_cast<int>(sourceComponent[0]) * 255 / alpha; 163 destinationComponent[1] = static_cast<int>(sourceComponent[1]) * 255 / alpha; 164 destinationComponent[2] = static_cast<int>(sourceComponent[2]) * 255 / alpha; 165 } else { 166 destinationComponent[0] = 0; 167 destinationComponent[1] = 0; 168 destinationComponent[2] = 0; 169 } 170 destinationComponent[3] = alpha; 171 sourceComponent += 4; 172 destinationComponent += 4; 173 } 174 } 175 } 176 copyImageBytes(m_unmultipliedImageResult.get(), destination, rect); 177 } 178 179 void FilterEffect::copyPremultipliedImage(ImageData* destination, const IntRect& rect) 180 { 181 ASSERT(hasResult()); 182 183 if (!m_premultipliedImageResult) { 184 // We prefer a conversion from the image buffer. 185 if (m_imageBufferResult) 186 m_premultipliedImageResult = m_imageBufferResult->getPremultipliedImageData(IntRect(IntPoint(), m_absolutePaintRect.size())); 187 else { 188 m_premultipliedImageResult = ImageData::create(m_absolutePaintRect.width(), m_absolutePaintRect.height()); 189 unsigned char* sourceComponent = m_unmultipliedImageResult->data()->data()->data(); 190 unsigned char* destinationComponent = m_premultipliedImageResult->data()->data()->data(); 191 unsigned char* end = sourceComponent + (m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4); 192 while (sourceComponent < end) { 193 int alpha = sourceComponent[3]; 194 destinationComponent[0] = static_cast<int>(sourceComponent[0]) * alpha / 255; 195 destinationComponent[1] = static_cast<int>(sourceComponent[1]) * alpha / 255; 196 destinationComponent[2] = static_cast<int>(sourceComponent[2]) * alpha / 255; 197 destinationComponent[3] = alpha; 198 sourceComponent += 4; 199 destinationComponent += 4; 200 } 201 } 202 } 203 copyImageBytes(m_premultipliedImageResult.get(), destination, rect); 204 } 205 206 ImageBuffer* FilterEffect::createImageBufferResult() 207 { 208 // Only one result type is allowed. 209 ASSERT(!hasResult()); 77 210 determineAbsolutePaintRect(); 78 211 if (m_absolutePaintRect.isEmpty()) 79 212 return 0; 80 m_effectBuffer = ImageBuffer::create(m_absolutePaintRect.size(), ColorSpaceLinearRGB); 81 if (!m_effectBuffer) 82 return 0; 83 return m_effectBuffer->context(); 213 m_imageBufferResult = ImageBuffer::create(m_absolutePaintRect.size(), ColorSpaceLinearRGB); 214 if (!m_imageBufferResult) 215 return 0; 216 ASSERT(m_imageBufferResult->context()); 217 return m_imageBufferResult.get(); 218 } 219 220 ImageData* FilterEffect::createUnmultipliedImageResult() 221 { 222 // Only one result type is allowed. 223 ASSERT(!hasResult()); 224 determineAbsolutePaintRect(); 225 if (m_absolutePaintRect.isEmpty()) 226 return 0; 227 m_unmultipliedImageResult = ImageData::create(m_absolutePaintRect.width(), m_absolutePaintRect.height()); 228 return m_unmultipliedImageResult.get(); 229 } 230 231 ImageData* FilterEffect::createPremultipliedImageResult() 232 { 233 // Only one result type is allowed. 234 ASSERT(!hasResult()); 235 determineAbsolutePaintRect(); 236 if (m_absolutePaintRect.isEmpty()) 237 return 0; 238 m_premultipliedImageResult = ImageData::create(m_absolutePaintRect.width(), m_absolutePaintRect.height()); 239 return m_premultipliedImageResult.get(); 84 240 } 85 241 -
trunk/WebCore/platform/graphics/filters/FilterEffect.h
r72474 r73894 51 51 virtual ~FilterEffect(); 52 52 53 // The result is bounded to the size of the filter primitive to save resources. 54 ImageBuffer* resultImage() const { return m_effectBuffer.get(); } 55 void setEffectBuffer(PassOwnPtr<ImageBuffer> effectBuffer) { m_effectBuffer = effectBuffer; } 56 57 // Creates the ImageBuffer for the current filter primitive result in the size of the 58 // repaintRect. Gives back the GraphicsContext of the own ImageBuffer. 59 GraphicsContext* effectContext(); 53 bool hasResult() const { return m_imageBufferResult || m_unmultipliedImageResult || m_premultipliedImageResult; } 54 ImageBuffer* asImageBuffer(); 55 PassRefPtr<ImageData> asUnmultipliedImage(const IntRect&); 56 PassRefPtr<ImageData> asPremultipliedImage(const IntRect&); 57 void copyUnmultipliedImage(ImageData* destination, const IntRect&); 58 void copyPremultipliedImage(ImageData* destination, const IntRect&); 60 59 61 60 FilterEffectVector& inputEffects() { return m_inputEffects; } … … 111 110 FilterEffect(Filter*); 112 111 112 ImageBuffer* createImageBufferResult(); 113 ImageData* createUnmultipliedImageResult(); 114 ImageData* createPremultipliedImageResult(); 115 113 116 private: 114 OwnPtr<ImageBuffer> m_effectBuffer; 117 OwnPtr<ImageBuffer> m_imageBufferResult; 118 RefPtr<ImageData> m_unmultipliedImageResult; 119 RefPtr<ImageData> m_premultipliedImageResult; 115 120 FilterEffectVector m_inputEffects; 116 121 … … 125 130 126 131 private: 132 inline void copyImageBytes(ImageData* source, ImageData* destination, const IntRect&); 133 127 134 // The following member variables are SVG specific and will move to RenderSVGResourceFilterPrimitive. 128 135 // See bug https://bugs.webkit.org/show_bug.cgi?id=45614. -
trunk/WebCore/platform/graphics/filters/SourceAlpha.cpp
r72474 r73894 53 53 void SourceAlpha::apply() 54 54 { 55 GraphicsContext* filterContext = effectContext(); 55 if (hasResult()) 56 return; 57 ImageBuffer* resultImage = createImageBufferResult(); 56 58 Filter* filter = this->filter(); 57 if (! filterContext|| !filter->sourceImage())59 if (!resultImage || !filter->sourceImage()) 58 60 return; 59 61 … … 61 63 62 64 FloatRect imageRect(FloatPoint(), absolutePaintRect().size()); 65 GraphicsContext* filterContext = resultImage->context(); 63 66 filterContext->save(); 64 67 filterContext->clipToImageBuffer(filter->sourceImage(), imageRect); -
trunk/WebCore/platform/graphics/filters/SourceGraphic.cpp
r72474 r73894 52 52 void SourceGraphic::apply() 53 53 { 54 GraphicsContext* filterContext = effectContext(); 54 if (hasResult()) 55 return; 56 ImageBuffer* resultImage = createImageBufferResult(); 55 57 Filter* filter = this->filter(); 56 if (! filterContext|| !filter->sourceImage())58 if (!resultImage || !filter->sourceImage()) 57 59 return; 58 60 59 filterContext->drawImageBuffer(filter->sourceImage(), ColorSpaceDeviceRGB, IntPoint());61 resultImage->context()->drawImageBuffer(filter->sourceImage(), ColorSpaceDeviceRGB, IntPoint()); 60 62 } 61 63 -
trunk/WebCore/platform/graphics/filters/SourceGraphic.h
r72474 r73894 20 20 21 21 #ifndef SourceGraphic_h 22 #define SourceGra hpic_h22 #define SourceGraphic_h 23 23 24 24 #if ENABLE(FILTERS) -
trunk/WebCore/rendering/RenderSVGResourceFilter.cpp
r73258 r73894 294 294 lastEffect->apply(); 295 295 #if !PLATFORM(CG) 296 ImageBuffer* resultImage = lastEffect-> resultImage();296 ImageBuffer* resultImage = lastEffect->asImageBuffer(); 297 297 if (resultImage) 298 298 resultImage->transformColorSpace(ColorSpaceLinearRGB, ColorSpaceDeviceRGB); … … 301 301 } 302 302 303 ImageBuffer* resultImage = lastEffect-> resultImage();303 ImageBuffer* resultImage = lastEffect->asImageBuffer(); 304 304 if (resultImage) { 305 305 context->concatCTM(filterData->shearFreeAbsoluteTransform.inverse()); -
trunk/WebCore/svg/graphics/filters/SVGFEImage.cpp
r72474 r73894 60 60 return; 61 61 62 GraphicsContext* filterContext = effectContext();63 if (! filterContext)62 ImageBuffer* resultImage = createImageBufferResult(); 63 if (!resultImage) 64 64 return; 65 65 … … 71 71 destRect.move(-paintLocation.x(), -paintLocation.y()); 72 72 73 filterContext->drawImage(m_image.get(), ColorSpaceDeviceRGB, destRect, srcRect);73 resultImage->context()->drawImage(m_image.get(), ColorSpaceDeviceRGB, destRect, srcRect); 74 74 } 75 75
Note: See TracChangeset
for help on using the changeset viewer.