michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "FilterSupport.h" michael@0: michael@0: #include "mozilla/gfx/2D.h" michael@0: #include "mozilla/gfx/Filters.h" michael@0: #include "mozilla/PodOperations.h" michael@0: michael@0: #include "gfxContext.h" michael@0: #include "gfxPattern.h" michael@0: #include "gfxPlatform.h" michael@0: #include "gfx2DGlue.h" michael@0: michael@0: #include "nsMargin.h" michael@0: michael@0: // c = n / 255 michael@0: // c <= 0.0031308f ? c * 12.92f : 1.055f * powf(c, 1 / 2.4f) - 0.055f michael@0: static const float glinearRGBTosRGBMap[256] = { michael@0: 0.000f, 0.050f, 0.085f, 0.111f, 0.132f, 0.150f, 0.166f, 0.181f, michael@0: 0.194f, 0.207f, 0.219f, 0.230f, 0.240f, 0.250f, 0.260f, 0.269f, michael@0: 0.278f, 0.286f, 0.295f, 0.303f, 0.310f, 0.318f, 0.325f, 0.332f, michael@0: 0.339f, 0.346f, 0.352f, 0.359f, 0.365f, 0.371f, 0.378f, 0.383f, michael@0: 0.389f, 0.395f, 0.401f, 0.406f, 0.412f, 0.417f, 0.422f, 0.427f, michael@0: 0.433f, 0.438f, 0.443f, 0.448f, 0.452f, 0.457f, 0.462f, 0.466f, michael@0: 0.471f, 0.476f, 0.480f, 0.485f, 0.489f, 0.493f, 0.498f, 0.502f, michael@0: 0.506f, 0.510f, 0.514f, 0.518f, 0.522f, 0.526f, 0.530f, 0.534f, michael@0: 0.538f, 0.542f, 0.546f, 0.549f, 0.553f, 0.557f, 0.561f, 0.564f, michael@0: 0.568f, 0.571f, 0.575f, 0.579f, 0.582f, 0.586f, 0.589f, 0.592f, michael@0: 0.596f, 0.599f, 0.603f, 0.606f, 0.609f, 0.613f, 0.616f, 0.619f, michael@0: 0.622f, 0.625f, 0.629f, 0.632f, 0.635f, 0.638f, 0.641f, 0.644f, michael@0: 0.647f, 0.650f, 0.653f, 0.656f, 0.659f, 0.662f, 0.665f, 0.668f, michael@0: 0.671f, 0.674f, 0.677f, 0.680f, 0.683f, 0.685f, 0.688f, 0.691f, michael@0: 0.694f, 0.697f, 0.699f, 0.702f, 0.705f, 0.708f, 0.710f, 0.713f, michael@0: 0.716f, 0.718f, 0.721f, 0.724f, 0.726f, 0.729f, 0.731f, 0.734f, michael@0: 0.737f, 0.739f, 0.742f, 0.744f, 0.747f, 0.749f, 0.752f, 0.754f, michael@0: 0.757f, 0.759f, 0.762f, 0.764f, 0.767f, 0.769f, 0.772f, 0.774f, michael@0: 0.776f, 0.779f, 0.781f, 0.784f, 0.786f, 0.788f, 0.791f, 0.793f, michael@0: 0.795f, 0.798f, 0.800f, 0.802f, 0.805f, 0.807f, 0.809f, 0.812f, michael@0: 0.814f, 0.816f, 0.818f, 0.821f, 0.823f, 0.825f, 0.827f, 0.829f, michael@0: 0.832f, 0.834f, 0.836f, 0.838f, 0.840f, 0.843f, 0.845f, 0.847f, michael@0: 0.849f, 0.851f, 0.853f, 0.855f, 0.857f, 0.860f, 0.862f, 0.864f, michael@0: 0.866f, 0.868f, 0.870f, 0.872f, 0.874f, 0.876f, 0.878f, 0.880f, michael@0: 0.882f, 0.884f, 0.886f, 0.888f, 0.890f, 0.892f, 0.894f, 0.896f, michael@0: 0.898f, 0.900f, 0.902f, 0.904f, 0.906f, 0.908f, 0.910f, 0.912f, michael@0: 0.914f, 0.916f, 0.918f, 0.920f, 0.922f, 0.924f, 0.926f, 0.928f, michael@0: 0.930f, 0.931f, 0.933f, 0.935f, 0.937f, 0.939f, 0.941f, 0.943f, michael@0: 0.945f, 0.946f, 0.948f, 0.950f, 0.952f, 0.954f, 0.956f, 0.957f, michael@0: 0.959f, 0.961f, 0.963f, 0.965f, 0.967f, 0.968f, 0.970f, 0.972f, michael@0: 0.974f, 0.975f, 0.977f, 0.979f, 0.981f, 0.983f, 0.984f, 0.986f, michael@0: 0.988f, 0.990f, 0.991f, 0.993f, 0.995f, 0.997f, 0.998f, 1.000f michael@0: }; michael@0: michael@0: // c = n / 255 michael@0: // c <= 0.04045f ? c / 12.92f : powf((c + 0.055f) / 1.055f, 2.4f) michael@0: static const float gsRGBToLinearRGBMap[256] = { michael@0: 0.000f, 0.000f, 0.001f, 0.001f, 0.001f, 0.002f, 0.002f, 0.002f, michael@0: 0.002f, 0.003f, 0.003f, 0.003f, 0.004f, 0.004f, 0.004f, 0.005f, michael@0: 0.005f, 0.006f, 0.006f, 0.007f, 0.007f, 0.007f, 0.008f, 0.009f, michael@0: 0.009f, 0.010f, 0.010f, 0.011f, 0.012f, 0.012f, 0.013f, 0.014f, michael@0: 0.014f, 0.015f, 0.016f, 0.017f, 0.018f, 0.019f, 0.019f, 0.020f, michael@0: 0.021f, 0.022f, 0.023f, 0.024f, 0.025f, 0.026f, 0.027f, 0.028f, michael@0: 0.030f, 0.031f, 0.032f, 0.033f, 0.034f, 0.036f, 0.037f, 0.038f, michael@0: 0.040f, 0.041f, 0.042f, 0.044f, 0.045f, 0.047f, 0.048f, 0.050f, michael@0: 0.051f, 0.053f, 0.054f, 0.056f, 0.058f, 0.060f, 0.061f, 0.063f, michael@0: 0.065f, 0.067f, 0.068f, 0.070f, 0.072f, 0.074f, 0.076f, 0.078f, michael@0: 0.080f, 0.082f, 0.084f, 0.087f, 0.089f, 0.091f, 0.093f, 0.095f, michael@0: 0.098f, 0.100f, 0.102f, 0.105f, 0.107f, 0.109f, 0.112f, 0.114f, michael@0: 0.117f, 0.120f, 0.122f, 0.125f, 0.127f, 0.130f, 0.133f, 0.136f, michael@0: 0.138f, 0.141f, 0.144f, 0.147f, 0.150f, 0.153f, 0.156f, 0.159f, michael@0: 0.162f, 0.165f, 0.168f, 0.171f, 0.175f, 0.178f, 0.181f, 0.184f, michael@0: 0.188f, 0.191f, 0.195f, 0.198f, 0.202f, 0.205f, 0.209f, 0.212f, michael@0: 0.216f, 0.220f, 0.223f, 0.227f, 0.231f, 0.235f, 0.238f, 0.242f, michael@0: 0.246f, 0.250f, 0.254f, 0.258f, 0.262f, 0.266f, 0.270f, 0.275f, michael@0: 0.279f, 0.283f, 0.287f, 0.292f, 0.296f, 0.301f, 0.305f, 0.309f, michael@0: 0.314f, 0.319f, 0.323f, 0.328f, 0.332f, 0.337f, 0.342f, 0.347f, michael@0: 0.352f, 0.356f, 0.361f, 0.366f, 0.371f, 0.376f, 0.381f, 0.386f, michael@0: 0.392f, 0.397f, 0.402f, 0.407f, 0.413f, 0.418f, 0.423f, 0.429f, michael@0: 0.434f, 0.440f, 0.445f, 0.451f, 0.456f, 0.462f, 0.468f, 0.474f, michael@0: 0.479f, 0.485f, 0.491f, 0.497f, 0.503f, 0.509f, 0.515f, 0.521f, michael@0: 0.527f, 0.533f, 0.539f, 0.546f, 0.552f, 0.558f, 0.565f, 0.571f, michael@0: 0.578f, 0.584f, 0.591f, 0.597f, 0.604f, 0.610f, 0.617f, 0.624f, michael@0: 0.631f, 0.638f, 0.644f, 0.651f, 0.658f, 0.665f, 0.672f, 0.680f, michael@0: 0.687f, 0.694f, 0.701f, 0.708f, 0.716f, 0.723f, 0.730f, 0.738f, michael@0: 0.745f, 0.753f, 0.761f, 0.768f, 0.776f, 0.784f, 0.791f, 0.799f, michael@0: 0.807f, 0.815f, 0.823f, 0.831f, 0.839f, 0.847f, 0.855f, 0.863f, michael@0: 0.871f, 0.880f, 0.888f, 0.896f, 0.905f, 0.913f, 0.922f, 0.930f, michael@0: 0.939f, 0.947f, 0.956f, 0.965f, 0.973f, 0.982f, 0.991f, 1.000f michael@0: }; michael@0: michael@0: namespace mozilla { michael@0: namespace gfx { michael@0: michael@0: // Some convenience FilterNode creation functions. michael@0: michael@0: static const float kMaxStdDeviation = 500; michael@0: michael@0: namespace FilterWrappers { michael@0: michael@0: static TemporaryRef michael@0: Unpremultiply(DrawTarget* aDT, FilterNode* aInput) michael@0: { michael@0: RefPtr filter = aDT->CreateFilter(FilterType::UNPREMULTIPLY); michael@0: filter->SetInput(IN_UNPREMULTIPLY_IN, aInput); michael@0: return filter; michael@0: } michael@0: michael@0: static TemporaryRef michael@0: Premultiply(DrawTarget* aDT, FilterNode* aInput) michael@0: { michael@0: RefPtr filter = aDT->CreateFilter(FilterType::PREMULTIPLY); michael@0: filter->SetInput(IN_PREMULTIPLY_IN, aInput); michael@0: return filter; michael@0: } michael@0: michael@0: static TemporaryRef michael@0: LinearRGBToSRGB(DrawTarget* aDT, FilterNode* aInput) michael@0: { michael@0: RefPtr transfer = aDT->CreateFilter(FilterType::DISCRETE_TRANSFER); michael@0: transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_R, false); michael@0: transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_R, glinearRGBTosRGBMap, 256); michael@0: transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_G, false); michael@0: transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_G, glinearRGBTosRGBMap, 256); michael@0: transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_B, false); michael@0: transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_B, glinearRGBTosRGBMap, 256); michael@0: transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_A, true); michael@0: transfer->SetInput(IN_DISCRETE_TRANSFER_IN, aInput); michael@0: return transfer; michael@0: } michael@0: michael@0: static TemporaryRef michael@0: SRGBToLinearRGB(DrawTarget* aDT, FilterNode* aInput) michael@0: { michael@0: RefPtr transfer = aDT->CreateFilter(FilterType::DISCRETE_TRANSFER); michael@0: transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_R, false); michael@0: transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_R, gsRGBToLinearRGBMap, 256); michael@0: transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_G, false); michael@0: transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_G, gsRGBToLinearRGBMap, 256); michael@0: transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_B, false); michael@0: transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_B, gsRGBToLinearRGBMap, 256); michael@0: transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_A, true); michael@0: transfer->SetInput(IN_DISCRETE_TRANSFER_IN, aInput); michael@0: return transfer; michael@0: } michael@0: michael@0: static TemporaryRef michael@0: Crop(DrawTarget* aDT, FilterNode* aInputFilter, const IntRect& aRect) michael@0: { michael@0: RefPtr filter = aDT->CreateFilter(FilterType::CROP); michael@0: filter->SetAttribute(ATT_CROP_RECT, Rect(aRect)); michael@0: filter->SetInput(IN_CROP_IN, aInputFilter); michael@0: return filter; michael@0: } michael@0: michael@0: static TemporaryRef michael@0: Offset(DrawTarget* aDT, FilterNode* aInputFilter, const IntPoint& aOffset) michael@0: { michael@0: RefPtr filter = aDT->CreateFilter(FilterType::TRANSFORM); michael@0: filter->SetAttribute(ATT_TRANSFORM_MATRIX, Matrix::Translation(aOffset.x, aOffset.y)); michael@0: filter->SetInput(IN_TRANSFORM_IN, aInputFilter); michael@0: return filter; michael@0: } michael@0: michael@0: static TemporaryRef michael@0: GaussianBlur(DrawTarget* aDT, FilterNode* aInputFilter, const Size& aStdDeviation) michael@0: { michael@0: float stdX = float(std::min(aStdDeviation.width, kMaxStdDeviation)); michael@0: float stdY = float(std::min(aStdDeviation.height, kMaxStdDeviation)); michael@0: if (stdX == stdY) { michael@0: RefPtr filter = aDT->CreateFilter(FilterType::GAUSSIAN_BLUR); michael@0: filter->SetAttribute(ATT_GAUSSIAN_BLUR_STD_DEVIATION, stdX); michael@0: filter->SetInput(IN_GAUSSIAN_BLUR_IN, aInputFilter); michael@0: return filter; michael@0: } michael@0: RefPtr filterH = aDT->CreateFilter(FilterType::DIRECTIONAL_BLUR); michael@0: RefPtr filterV = aDT->CreateFilter(FilterType::DIRECTIONAL_BLUR); michael@0: filterH->SetAttribute(ATT_DIRECTIONAL_BLUR_DIRECTION, (uint32_t)BLUR_DIRECTION_X); michael@0: filterH->SetAttribute(ATT_DIRECTIONAL_BLUR_STD_DEVIATION, stdX); michael@0: filterV->SetAttribute(ATT_DIRECTIONAL_BLUR_DIRECTION, (uint32_t)BLUR_DIRECTION_Y); michael@0: filterV->SetAttribute(ATT_DIRECTIONAL_BLUR_STD_DEVIATION, stdY); michael@0: filterH->SetInput(IN_DIRECTIONAL_BLUR_IN, aInputFilter); michael@0: filterV->SetInput(IN_DIRECTIONAL_BLUR_IN, filterH); michael@0: return filterV; michael@0: } michael@0: michael@0: static TemporaryRef michael@0: Clear(DrawTarget* aDT) michael@0: { michael@0: RefPtr filter = aDT->CreateFilter(FilterType::FLOOD); michael@0: filter->SetAttribute(ATT_FLOOD_COLOR, Color(0,0,0,0)); michael@0: return filter; michael@0: } michael@0: michael@0: static TemporaryRef michael@0: ForSurface(DrawTarget* aDT, SourceSurface* aSurface, michael@0: const IntPoint& aSurfacePosition) michael@0: { michael@0: RefPtr filter = aDT->CreateFilter(FilterType::TRANSFORM); michael@0: filter->SetAttribute(ATT_TRANSFORM_MATRIX, michael@0: Matrix::Translation(aSurfacePosition.x, aSurfacePosition.y)); michael@0: filter->SetInput(IN_TRANSFORM_IN, aSurface); michael@0: return filter; michael@0: } michael@0: michael@0: static TemporaryRef michael@0: ToAlpha(DrawTarget* aDT, FilterNode* aInput) michael@0: { michael@0: float zero = 0.0f; michael@0: RefPtr transfer = aDT->CreateFilter(FilterType::DISCRETE_TRANSFER); michael@0: transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_R, false); michael@0: transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_R, &zero, 1); michael@0: transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_G, false); michael@0: transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_G, &zero, 1); michael@0: transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_B, false); michael@0: transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_B, &zero, 1); michael@0: transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_A, true); michael@0: transfer->SetInput(IN_DISCRETE_TRANSFER_IN, aInput); michael@0: return transfer; michael@0: } michael@0: michael@0: } michael@0: michael@0: // A class that wraps a FilterNode and handles conversion between different michael@0: // color models. Create FilterCachedColorModels with your original filter and michael@0: // the color model that this filter outputs in natively, and then call michael@0: // ->ForColorModel(colorModel) in order to get a FilterNode which outputs to michael@0: // the specified colorModel. michael@0: // Internally, this is achieved by wrapping the original FilterNode with michael@0: // conversion FilterNodes. These filter nodes are cached in such a way that no michael@0: // repeated or back-and-forth conversions happen. michael@0: class FilterCachedColorModels michael@0: { michael@0: public: michael@0: NS_INLINE_DECL_REFCOUNTING(FilterCachedColorModels) michael@0: // aFilter can be null. In that case, ForColorModel will return a non-null michael@0: // completely transparent filter for all color models. michael@0: FilterCachedColorModels(DrawTarget* aDT, michael@0: FilterNode* aFilter, michael@0: ColorModel aOriginalColorModel); michael@0: michael@0: // Get a FilterNode for the specified color model, guaranteed to be non-null. michael@0: TemporaryRef ForColorModel(ColorModel aColorModel); michael@0: michael@0: AlphaModel OriginalAlphaModel() const { return mOriginalColorModel.mAlphaModel; } michael@0: michael@0: private: michael@0: // Create the required FilterNode that will be cached by ForColorModel. michael@0: TemporaryRef WrapForColorModel(ColorModel aColorModel); michael@0: michael@0: RefPtr mDT; michael@0: ColorModel mOriginalColorModel; michael@0: michael@0: // This array is indexed by ColorModel::ToIndex. michael@0: RefPtr mFilterForColorModel[4]; michael@0: }; michael@0: michael@0: FilterCachedColorModels::FilterCachedColorModels(DrawTarget* aDT, michael@0: FilterNode* aFilter, michael@0: ColorModel aOriginalColorModel) michael@0: : mDT(aDT) michael@0: , mOriginalColorModel(aOriginalColorModel) michael@0: { michael@0: if (aFilter) { michael@0: mFilterForColorModel[aOriginalColorModel.ToIndex()] = aFilter; michael@0: } else { michael@0: RefPtr clear = FilterWrappers::Clear(aDT); michael@0: mFilterForColorModel[0] = clear; michael@0: mFilterForColorModel[1] = clear; michael@0: mFilterForColorModel[2] = clear; michael@0: mFilterForColorModel[3] = clear; michael@0: } michael@0: } michael@0: michael@0: TemporaryRef michael@0: FilterCachedColorModels::ForColorModel(ColorModel aColorModel) michael@0: { michael@0: if (!mFilterForColorModel[aColorModel.ToIndex()]) { michael@0: mFilterForColorModel[aColorModel.ToIndex()] = WrapForColorModel(aColorModel); michael@0: } michael@0: return mFilterForColorModel[aColorModel.ToIndex()]; michael@0: } michael@0: michael@0: TemporaryRef michael@0: FilterCachedColorModels::WrapForColorModel(ColorModel aColorModel) michael@0: { michael@0: // Convert one aspect at a time and recurse. michael@0: // Conversions between premultiplied / unpremultiplied color channels for the michael@0: // same color space can happen directly. michael@0: // Conversions between different color spaces can only happen on michael@0: // unpremultiplied color channels. michael@0: michael@0: if (aColorModel.mAlphaModel == AlphaModel::Premultiplied) { michael@0: RefPtr unpre = michael@0: ForColorModel(ColorModel(aColorModel.mColorSpace, AlphaModel::Unpremultiplied)); michael@0: return FilterWrappers::Premultiply(mDT, unpre); michael@0: } michael@0: michael@0: MOZ_ASSERT(aColorModel.mAlphaModel == AlphaModel::Unpremultiplied); michael@0: if (aColorModel.mColorSpace == mOriginalColorModel.mColorSpace) { michael@0: RefPtr premultiplied = michael@0: ForColorModel(ColorModel(aColorModel.mColorSpace, AlphaModel::Premultiplied)); michael@0: return FilterWrappers::Unpremultiply(mDT, premultiplied); michael@0: } michael@0: michael@0: RefPtr unpremultipliedOriginal = michael@0: ForColorModel(ColorModel(mOriginalColorModel.mColorSpace, AlphaModel::Unpremultiplied)); michael@0: if (aColorModel.mColorSpace == ColorSpace::LinearRGB) { michael@0: return FilterWrappers::SRGBToLinearRGB(mDT, unpremultipliedOriginal); michael@0: } michael@0: return FilterWrappers::LinearRGBToSRGB(mDT, unpremultipliedOriginal); michael@0: } michael@0: michael@0: // Create a 4x5 color matrix for the different ways to specify color matrices michael@0: // in SVG. michael@0: static nsresult michael@0: ComputeColorMatrix(uint32_t aColorMatrixType, const nsTArray& aValues, michael@0: float aOutMatrix[20]) michael@0: { michael@0: static const float identityMatrix[] = michael@0: { 1, 0, 0, 0, 0, michael@0: 0, 1, 0, 0, 0, michael@0: 0, 0, 1, 0, 0, michael@0: 0, 0, 0, 1, 0 }; michael@0: michael@0: static const float luminanceToAlphaMatrix[] = michael@0: { 0, 0, 0, 0, 0, michael@0: 0, 0, 0, 0, 0, michael@0: 0, 0, 0, 0, 0, michael@0: 0.2125f, 0.7154f, 0.0721f, 0, 0 }; michael@0: michael@0: switch (aColorMatrixType) { michael@0: michael@0: case SVG_FECOLORMATRIX_TYPE_MATRIX: michael@0: { michael@0: if (aValues.Length() != 20) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: PodCopy(aOutMatrix, aValues.Elements(), 20); michael@0: break; michael@0: } michael@0: michael@0: case SVG_FECOLORMATRIX_TYPE_SATURATE: michael@0: { michael@0: if (aValues.Length() != 1) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: float s = aValues[0]; michael@0: michael@0: if (s < 0) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: PodCopy(aOutMatrix, identityMatrix, 20); michael@0: michael@0: aOutMatrix[0] = 0.213f + 0.787f * s; michael@0: aOutMatrix[1] = 0.715f - 0.715f * s; michael@0: aOutMatrix[2] = 0.072f - 0.072f * s; michael@0: michael@0: aOutMatrix[5] = 0.213f - 0.213f * s; michael@0: aOutMatrix[6] = 0.715f + 0.285f * s; michael@0: aOutMatrix[7] = 0.072f - 0.072f * s; michael@0: michael@0: aOutMatrix[10] = 0.213f - 0.213f * s; michael@0: aOutMatrix[11] = 0.715f - 0.715f * s; michael@0: aOutMatrix[12] = 0.072f + 0.928f * s; michael@0: michael@0: break; michael@0: } michael@0: michael@0: case SVG_FECOLORMATRIX_TYPE_HUE_ROTATE: michael@0: { michael@0: if (aValues.Length() != 1) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: PodCopy(aOutMatrix, identityMatrix, 20); michael@0: michael@0: float hueRotateValue = aValues[0]; michael@0: michael@0: float c = static_cast(cos(hueRotateValue * M_PI / 180)); michael@0: float s = static_cast(sin(hueRotateValue * M_PI / 180)); michael@0: michael@0: aOutMatrix[0] = 0.213f + 0.787f * c - 0.213f * s; michael@0: aOutMatrix[1] = 0.715f - 0.715f * c - 0.715f * s; michael@0: aOutMatrix[2] = 0.072f - 0.072f * c + 0.928f * s; michael@0: michael@0: aOutMatrix[5] = 0.213f - 0.213f * c + 0.143f * s; michael@0: aOutMatrix[6] = 0.715f + 0.285f * c + 0.140f * s; michael@0: aOutMatrix[7] = 0.072f - 0.072f * c - 0.283f * s; michael@0: michael@0: aOutMatrix[10] = 0.213f - 0.213f * c - 0.787f * s; michael@0: aOutMatrix[11] = 0.715f - 0.715f * c + 0.715f * s; michael@0: aOutMatrix[12] = 0.072f + 0.928f * c + 0.072f * s; michael@0: michael@0: break; michael@0: } michael@0: michael@0: case SVG_FECOLORMATRIX_TYPE_LUMINANCE_TO_ALPHA: michael@0: { michael@0: PodCopy(aOutMatrix, luminanceToAlphaMatrix, 20); michael@0: break; michael@0: } michael@0: michael@0: default: michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: static void michael@0: DisableAllTransfers(FilterNode* aTransferFilterNode) michael@0: { michael@0: aTransferFilterNode->SetAttribute(ATT_TRANSFER_DISABLE_R, true); michael@0: aTransferFilterNode->SetAttribute(ATT_TRANSFER_DISABLE_G, true); michael@0: aTransferFilterNode->SetAttribute(ATT_TRANSFER_DISABLE_B, true); michael@0: aTransferFilterNode->SetAttribute(ATT_TRANSFER_DISABLE_A, true); michael@0: } michael@0: michael@0: // Called for one channel at a time. michael@0: // This function creates the required FilterNodes on demand and tries to michael@0: // merge conversions of different channels into the same FilterNode if michael@0: // possible. michael@0: // There's a mismatch between the way SVG and the Moz2D API handle transfer michael@0: // functions: In SVG, it's possible to specify a different transfer function michael@0: // type for each color channel, but in Moz2D, a given transfer function type michael@0: // applies to all color channels. michael@0: // michael@0: // @param aFunctionAttributes The attributes of the transfer function for this michael@0: // channel. michael@0: // @param aChannel The color channel that this function applies to, where michael@0: // 0 = red, 1 = green, 2 = blue, 3 = alpha michael@0: // @param aDT The DrawTarget that the FilterNodes should be created for. michael@0: // @param aTableTransfer Existing FilterNode holders (which may still be michael@0: // null) that the resulting FilterNodes from this michael@0: // function will be stored in. michael@0: // michael@0: static void michael@0: ConvertComponentTransferFunctionToFilter(const AttributeMap& aFunctionAttributes, michael@0: int32_t aChannel, michael@0: DrawTarget* aDT, michael@0: RefPtr& aTableTransfer, michael@0: RefPtr& aDiscreteTransfer, michael@0: RefPtr& aLinearTransfer, michael@0: RefPtr& aGammaTransfer) michael@0: { michael@0: static const TransferAtts disableAtt[4] = { michael@0: ATT_TRANSFER_DISABLE_R, michael@0: ATT_TRANSFER_DISABLE_G, michael@0: ATT_TRANSFER_DISABLE_B, michael@0: ATT_TRANSFER_DISABLE_A michael@0: }; michael@0: michael@0: RefPtr filter; michael@0: michael@0: uint32_t type = aFunctionAttributes.GetUint(eComponentTransferFunctionType); michael@0: michael@0: switch (type) { michael@0: case SVG_FECOMPONENTTRANSFER_TYPE_TABLE: michael@0: { michael@0: const nsTArray& tableValues = michael@0: aFunctionAttributes.GetFloats(eComponentTransferFunctionTableValues); michael@0: if (tableValues.Length() < 2) michael@0: return; michael@0: michael@0: if (!aTableTransfer) { michael@0: aTableTransfer = aDT->CreateFilter(FilterType::TABLE_TRANSFER); michael@0: DisableAllTransfers(aTableTransfer); michael@0: } michael@0: filter = aTableTransfer; michael@0: static const TableTransferAtts tableAtt[4] = { michael@0: ATT_TABLE_TRANSFER_TABLE_R, michael@0: ATT_TABLE_TRANSFER_TABLE_G, michael@0: ATT_TABLE_TRANSFER_TABLE_B, michael@0: ATT_TABLE_TRANSFER_TABLE_A michael@0: }; michael@0: filter->SetAttribute(disableAtt[aChannel], false); michael@0: filter->SetAttribute(tableAtt[aChannel], &tableValues[0], tableValues.Length()); michael@0: break; michael@0: } michael@0: michael@0: case SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE: michael@0: { michael@0: const nsTArray& tableValues = michael@0: aFunctionAttributes.GetFloats(eComponentTransferFunctionTableValues); michael@0: if (tableValues.Length() < 1) michael@0: return; michael@0: michael@0: if (!aDiscreteTransfer) { michael@0: aDiscreteTransfer = aDT->CreateFilter(FilterType::DISCRETE_TRANSFER); michael@0: DisableAllTransfers(aDiscreteTransfer); michael@0: } michael@0: filter = aDiscreteTransfer; michael@0: static const DiscreteTransferAtts tableAtt[4] = { michael@0: ATT_DISCRETE_TRANSFER_TABLE_R, michael@0: ATT_DISCRETE_TRANSFER_TABLE_G, michael@0: ATT_DISCRETE_TRANSFER_TABLE_B, michael@0: ATT_DISCRETE_TRANSFER_TABLE_A michael@0: }; michael@0: filter->SetAttribute(disableAtt[aChannel], false); michael@0: filter->SetAttribute(tableAtt[aChannel], &tableValues[0], tableValues.Length()); michael@0: michael@0: break; michael@0: } michael@0: michael@0: case SVG_FECOMPONENTTRANSFER_TYPE_LINEAR: michael@0: { michael@0: static const LinearTransferAtts slopeAtt[4] = { michael@0: ATT_LINEAR_TRANSFER_SLOPE_R, michael@0: ATT_LINEAR_TRANSFER_SLOPE_G, michael@0: ATT_LINEAR_TRANSFER_SLOPE_B, michael@0: ATT_LINEAR_TRANSFER_SLOPE_A michael@0: }; michael@0: static const LinearTransferAtts interceptAtt[4] = { michael@0: ATT_LINEAR_TRANSFER_INTERCEPT_R, michael@0: ATT_LINEAR_TRANSFER_INTERCEPT_G, michael@0: ATT_LINEAR_TRANSFER_INTERCEPT_B, michael@0: ATT_LINEAR_TRANSFER_INTERCEPT_A michael@0: }; michael@0: if (!aLinearTransfer) { michael@0: aLinearTransfer = aDT->CreateFilter(FilterType::LINEAR_TRANSFER); michael@0: DisableAllTransfers(aLinearTransfer); michael@0: } michael@0: filter = aLinearTransfer; michael@0: filter->SetAttribute(disableAtt[aChannel], false); michael@0: float slope = aFunctionAttributes.GetFloat(eComponentTransferFunctionSlope); michael@0: float intercept = aFunctionAttributes.GetFloat(eComponentTransferFunctionIntercept); michael@0: filter->SetAttribute(slopeAtt[aChannel], slope); michael@0: filter->SetAttribute(interceptAtt[aChannel], intercept); michael@0: break; michael@0: } michael@0: michael@0: case SVG_FECOMPONENTTRANSFER_TYPE_GAMMA: michael@0: { michael@0: static const GammaTransferAtts amplitudeAtt[4] = { michael@0: ATT_GAMMA_TRANSFER_AMPLITUDE_R, michael@0: ATT_GAMMA_TRANSFER_AMPLITUDE_G, michael@0: ATT_GAMMA_TRANSFER_AMPLITUDE_B, michael@0: ATT_GAMMA_TRANSFER_AMPLITUDE_A michael@0: }; michael@0: static const GammaTransferAtts exponentAtt[4] = { michael@0: ATT_GAMMA_TRANSFER_EXPONENT_R, michael@0: ATT_GAMMA_TRANSFER_EXPONENT_G, michael@0: ATT_GAMMA_TRANSFER_EXPONENT_B, michael@0: ATT_GAMMA_TRANSFER_EXPONENT_A michael@0: }; michael@0: static const GammaTransferAtts offsetAtt[4] = { michael@0: ATT_GAMMA_TRANSFER_OFFSET_R, michael@0: ATT_GAMMA_TRANSFER_OFFSET_G, michael@0: ATT_GAMMA_TRANSFER_OFFSET_B, michael@0: ATT_GAMMA_TRANSFER_OFFSET_A michael@0: }; michael@0: if (!aGammaTransfer) { michael@0: aGammaTransfer = aDT->CreateFilter(FilterType::GAMMA_TRANSFER); michael@0: DisableAllTransfers(aGammaTransfer); michael@0: } michael@0: filter = aGammaTransfer; michael@0: filter->SetAttribute(disableAtt[aChannel], false); michael@0: float amplitude = aFunctionAttributes.GetFloat(eComponentTransferFunctionAmplitude); michael@0: float exponent = aFunctionAttributes.GetFloat(eComponentTransferFunctionExponent); michael@0: float offset = aFunctionAttributes.GetFloat(eComponentTransferFunctionOffset); michael@0: filter->SetAttribute(amplitudeAtt[aChannel], amplitude); michael@0: filter->SetAttribute(exponentAtt[aChannel], exponent); michael@0: filter->SetAttribute(offsetAtt[aChannel], offset); michael@0: break; michael@0: } michael@0: michael@0: case SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY: michael@0: default: michael@0: break; michael@0: } michael@0: } michael@0: michael@0: const int32_t kMorphologyMaxRadius = 100000; michael@0: michael@0: // Handle the different primitive description types and create the necessary michael@0: // FilterNode(s) for each. michael@0: // Returns nullptr for invalid filter primitives. This should be interpreted as michael@0: // transparent black by the caller. michael@0: // aSourceRegions contains the filter primitive subregions of the source michael@0: // primitives; only needed for eTile primitives. michael@0: // aInputImages carries additional surfaces that are used by eImage primitives. michael@0: static TemporaryRef michael@0: FilterNodeFromPrimitiveDescription(const FilterPrimitiveDescription& aDescription, michael@0: DrawTarget* aDT, michael@0: nsTArray >& aSources, michael@0: nsTArray& aSourceRegions, michael@0: nsTArray>& aInputImages) michael@0: { michael@0: const AttributeMap& atts = aDescription.Attributes(); michael@0: switch (aDescription.Type()) { michael@0: michael@0: case PrimitiveType::Empty: michael@0: return nullptr; michael@0: michael@0: case PrimitiveType::Blend: michael@0: { michael@0: uint32_t mode = atts.GetUint(eBlendBlendmode); michael@0: RefPtr filter; michael@0: if (mode == SVG_FEBLEND_MODE_UNKNOWN) { michael@0: return nullptr; michael@0: } michael@0: if (mode == SVG_FEBLEND_MODE_NORMAL) { michael@0: filter = aDT->CreateFilter(FilterType::COMPOSITE); michael@0: filter->SetInput(IN_COMPOSITE_IN_START, aSources[1]); michael@0: filter->SetInput(IN_COMPOSITE_IN_START + 1, aSources[0]); michael@0: } else { michael@0: filter = aDT->CreateFilter(FilterType::BLEND); michael@0: static const uint8_t blendModes[SVG_FEBLEND_MODE_LIGHTEN + 1] = { michael@0: 0, michael@0: 0, michael@0: BLEND_MODE_MULTIPLY, michael@0: BLEND_MODE_SCREEN, michael@0: BLEND_MODE_DARKEN, michael@0: BLEND_MODE_LIGHTEN michael@0: }; michael@0: filter->SetAttribute(ATT_BLEND_BLENDMODE, (uint32_t)blendModes[mode]); michael@0: filter->SetInput(IN_BLEND_IN, aSources[0]); michael@0: filter->SetInput(IN_BLEND_IN2, aSources[1]); michael@0: } michael@0: return filter; michael@0: } michael@0: michael@0: case PrimitiveType::ColorMatrix: michael@0: { michael@0: float colorMatrix[20]; michael@0: uint32_t type = atts.GetUint(eColorMatrixType); michael@0: const nsTArray& values = atts.GetFloats(eColorMatrixValues); michael@0: if (NS_FAILED(ComputeColorMatrix(type, values, colorMatrix))) { michael@0: return aSources[0]; michael@0: } michael@0: Matrix5x4 matrix(colorMatrix[0], colorMatrix[5], colorMatrix[10], colorMatrix[15], michael@0: colorMatrix[1], colorMatrix[6], colorMatrix[11], colorMatrix[16], michael@0: colorMatrix[2], colorMatrix[7], colorMatrix[12], colorMatrix[17], michael@0: colorMatrix[3], colorMatrix[8], colorMatrix[13], colorMatrix[18], michael@0: colorMatrix[4], colorMatrix[9], colorMatrix[14], colorMatrix[19]); michael@0: RefPtr filter = aDT->CreateFilter(FilterType::COLOR_MATRIX); michael@0: filter->SetAttribute(ATT_COLOR_MATRIX_MATRIX, matrix); michael@0: filter->SetAttribute(ATT_COLOR_MATRIX_ALPHA_MODE, (uint32_t)ALPHA_MODE_STRAIGHT); michael@0: filter->SetInput(IN_COLOR_MATRIX_IN, aSources[0]); michael@0: return filter; michael@0: } michael@0: michael@0: case PrimitiveType::Morphology: michael@0: { michael@0: Size radii = atts.GetSize(eMorphologyRadii); michael@0: int32_t rx = radii.width; michael@0: int32_t ry = radii.height; michael@0: if (rx < 0 || ry < 0) { michael@0: // XXX SVGContentUtils::ReportToConsole() michael@0: return nullptr; michael@0: } michael@0: if (rx == 0 && ry == 0) { michael@0: return nullptr; michael@0: } michael@0: michael@0: // Clamp radii to prevent completely insane values: michael@0: rx = std::min(rx, kMorphologyMaxRadius); michael@0: ry = std::min(ry, kMorphologyMaxRadius); michael@0: michael@0: MorphologyOperator op = atts.GetUint(eMorphologyOperator) == SVG_OPERATOR_ERODE ? michael@0: MORPHOLOGY_OPERATOR_ERODE : MORPHOLOGY_OPERATOR_DILATE; michael@0: michael@0: RefPtr filter = aDT->CreateFilter(FilterType::MORPHOLOGY); michael@0: filter->SetAttribute(ATT_MORPHOLOGY_RADII, IntSize(rx, ry)); michael@0: filter->SetAttribute(ATT_MORPHOLOGY_OPERATOR, (uint32_t)op); michael@0: filter->SetInput(IN_MORPHOLOGY_IN, aSources[0]); michael@0: return filter; michael@0: } michael@0: michael@0: case PrimitiveType::Flood: michael@0: { michael@0: Color color = atts.GetColor(eFloodColor); michael@0: RefPtr filter = aDT->CreateFilter(FilterType::FLOOD); michael@0: filter->SetAttribute(ATT_FLOOD_COLOR, color); michael@0: return filter; michael@0: } michael@0: michael@0: case PrimitiveType::Tile: michael@0: { michael@0: RefPtr filter = aDT->CreateFilter(FilterType::TILE); michael@0: filter->SetAttribute(ATT_TILE_SOURCE_RECT, aSourceRegions[0]); michael@0: filter->SetInput(IN_TILE_IN, aSources[0]); michael@0: return filter; michael@0: } michael@0: michael@0: case PrimitiveType::ComponentTransfer: michael@0: { michael@0: RefPtr filters[4]; // one for each FILTER_*_TRANSFER type michael@0: static const AttributeName componentFunctionNames[4] = { michael@0: eComponentTransferFunctionR, michael@0: eComponentTransferFunctionG, michael@0: eComponentTransferFunctionB, michael@0: eComponentTransferFunctionA michael@0: }; michael@0: for (int32_t i = 0; i < 4; i++) { michael@0: AttributeMap functionAttributes = michael@0: atts.GetAttributeMap(componentFunctionNames[i]); michael@0: ConvertComponentTransferFunctionToFilter(functionAttributes, i, aDT, michael@0: filters[0], filters[1], filters[2], filters[3]); michael@0: } michael@0: michael@0: // Connect all used filters nodes. michael@0: RefPtr lastFilter = aSources[0]; michael@0: for (int32_t i = 0; i < 4; i++) { michael@0: if (filters[i]) { michael@0: filters[i]->SetInput(0, lastFilter); michael@0: lastFilter = filters[i]; michael@0: } michael@0: } michael@0: michael@0: return lastFilter; michael@0: } michael@0: michael@0: case PrimitiveType::ConvolveMatrix: michael@0: { michael@0: RefPtr filter = aDT->CreateFilter(FilterType::CONVOLVE_MATRIX); michael@0: filter->SetAttribute(ATT_CONVOLVE_MATRIX_KERNEL_SIZE, atts.GetIntSize(eConvolveMatrixKernelSize)); michael@0: const nsTArray& matrix = atts.GetFloats(eConvolveMatrixKernelMatrix); michael@0: filter->SetAttribute(ATT_CONVOLVE_MATRIX_KERNEL_MATRIX, michael@0: matrix.Elements(), matrix.Length()); michael@0: filter->SetAttribute(ATT_CONVOLVE_MATRIX_DIVISOR, michael@0: atts.GetFloat(eConvolveMatrixDivisor)); michael@0: filter->SetAttribute(ATT_CONVOLVE_MATRIX_BIAS, michael@0: atts.GetFloat(eConvolveMatrixBias)); michael@0: filter->SetAttribute(ATT_CONVOLVE_MATRIX_TARGET, michael@0: atts.GetIntPoint(eConvolveMatrixTarget)); michael@0: filter->SetAttribute(ATT_CONVOLVE_MATRIX_SOURCE_RECT, michael@0: aSourceRegions[0]); michael@0: uint32_t edgeMode = atts.GetUint(eConvolveMatrixEdgeMode); michael@0: static const uint8_t edgeModes[SVG_EDGEMODE_NONE+1] = { michael@0: EDGE_MODE_NONE, // SVG_EDGEMODE_UNKNOWN michael@0: EDGE_MODE_DUPLICATE, // SVG_EDGEMODE_DUPLICATE michael@0: EDGE_MODE_WRAP, // SVG_EDGEMODE_WRAP michael@0: EDGE_MODE_NONE // SVG_EDGEMODE_NONE michael@0: }; michael@0: filter->SetAttribute(ATT_CONVOLVE_MATRIX_EDGE_MODE, (uint32_t)edgeModes[edgeMode]); michael@0: filter->SetAttribute(ATT_CONVOLVE_MATRIX_KERNEL_UNIT_LENGTH, michael@0: atts.GetSize(eConvolveMatrixKernelUnitLength)); michael@0: filter->SetAttribute(ATT_CONVOLVE_MATRIX_PRESERVE_ALPHA, michael@0: atts.GetBool(eConvolveMatrixPreserveAlpha)); michael@0: filter->SetInput(IN_CONVOLVE_MATRIX_IN, aSources[0]); michael@0: return filter; michael@0: } michael@0: michael@0: case PrimitiveType::Offset: michael@0: { michael@0: return FilterWrappers::Offset(aDT, aSources[0], michael@0: atts.GetIntPoint(eOffsetOffset)); michael@0: } michael@0: michael@0: case PrimitiveType::DisplacementMap: michael@0: { michael@0: RefPtr filter = aDT->CreateFilter(FilterType::DISPLACEMENT_MAP); michael@0: filter->SetAttribute(ATT_DISPLACEMENT_MAP_SCALE, michael@0: atts.GetFloat(eDisplacementMapScale)); michael@0: static const uint8_t channel[SVG_CHANNEL_A+1] = { michael@0: COLOR_CHANNEL_R, // SVG_CHANNEL_UNKNOWN michael@0: COLOR_CHANNEL_R, // SVG_CHANNEL_R michael@0: COLOR_CHANNEL_G, // SVG_CHANNEL_G michael@0: COLOR_CHANNEL_B, // SVG_CHANNEL_B michael@0: COLOR_CHANNEL_A // SVG_CHANNEL_A michael@0: }; michael@0: filter->SetAttribute(ATT_DISPLACEMENT_MAP_X_CHANNEL, michael@0: (uint32_t)channel[atts.GetUint(eDisplacementMapXChannel)]); michael@0: filter->SetAttribute(ATT_DISPLACEMENT_MAP_Y_CHANNEL, michael@0: (uint32_t)channel[atts.GetUint(eDisplacementMapYChannel)]); michael@0: filter->SetInput(IN_DISPLACEMENT_MAP_IN, aSources[0]); michael@0: filter->SetInput(IN_DISPLACEMENT_MAP_IN2, aSources[1]); michael@0: return filter; michael@0: } michael@0: michael@0: case PrimitiveType::Turbulence: michael@0: { michael@0: RefPtr filter = aDT->CreateFilter(FilterType::TURBULENCE); michael@0: filter->SetAttribute(ATT_TURBULENCE_BASE_FREQUENCY, michael@0: atts.GetSize(eTurbulenceBaseFrequency)); michael@0: filter->SetAttribute(ATT_TURBULENCE_NUM_OCTAVES, michael@0: atts.GetUint(eTurbulenceNumOctaves)); michael@0: filter->SetAttribute(ATT_TURBULENCE_STITCHABLE, michael@0: atts.GetBool(eTurbulenceStitchable)); michael@0: filter->SetAttribute(ATT_TURBULENCE_SEED, michael@0: (uint32_t)atts.GetFloat(eTurbulenceSeed)); michael@0: static const uint8_t type[SVG_TURBULENCE_TYPE_TURBULENCE+1] = { michael@0: TURBULENCE_TYPE_FRACTAL_NOISE, // SVG_TURBULENCE_TYPE_UNKNOWN michael@0: TURBULENCE_TYPE_FRACTAL_NOISE, // SVG_TURBULENCE_TYPE_FRACTALNOISE michael@0: TURBULENCE_TYPE_TURBULENCE // SVG_TURBULENCE_TYPE_TURBULENCE michael@0: }; michael@0: filter->SetAttribute(ATT_TURBULENCE_TYPE, michael@0: (uint32_t)type[atts.GetUint(eTurbulenceType)]); michael@0: filter->SetAttribute(ATT_TURBULENCE_RECT, michael@0: aDescription.PrimitiveSubregion() - atts.GetIntPoint(eTurbulenceOffset)); michael@0: return FilterWrappers::Offset(aDT, filter, atts.GetIntPoint(eTurbulenceOffset)); michael@0: } michael@0: michael@0: case PrimitiveType::Composite: michael@0: { michael@0: RefPtr filter; michael@0: uint32_t op = atts.GetUint(eCompositeOperator); michael@0: if (op == SVG_FECOMPOSITE_OPERATOR_ARITHMETIC) { michael@0: filter = aDT->CreateFilter(FilterType::ARITHMETIC_COMBINE); michael@0: const nsTArray& coefficients = atts.GetFloats(eCompositeCoefficients); michael@0: filter->SetAttribute(ATT_ARITHMETIC_COMBINE_COEFFICIENTS, michael@0: coefficients.Elements(), coefficients.Length()); michael@0: filter->SetInput(IN_ARITHMETIC_COMBINE_IN, aSources[0]); michael@0: filter->SetInput(IN_ARITHMETIC_COMBINE_IN2, aSources[1]); michael@0: } else { michael@0: filter = aDT->CreateFilter(FilterType::COMPOSITE); michael@0: static const uint8_t operators[SVG_FECOMPOSITE_OPERATOR_ARITHMETIC] = { michael@0: COMPOSITE_OPERATOR_OVER, // SVG_FECOMPOSITE_OPERATOR_UNKNOWN michael@0: COMPOSITE_OPERATOR_OVER, // SVG_FECOMPOSITE_OPERATOR_OVER michael@0: COMPOSITE_OPERATOR_IN, // SVG_FECOMPOSITE_OPERATOR_IN michael@0: COMPOSITE_OPERATOR_OUT, // SVG_FECOMPOSITE_OPERATOR_OUT michael@0: COMPOSITE_OPERATOR_ATOP, // SVG_FECOMPOSITE_OPERATOR_ATOP michael@0: COMPOSITE_OPERATOR_XOR // SVG_FECOMPOSITE_OPERATOR_XOR michael@0: }; michael@0: filter->SetAttribute(ATT_COMPOSITE_OPERATOR, (uint32_t)operators[op]); michael@0: filter->SetInput(IN_COMPOSITE_IN_START, aSources[1]); michael@0: filter->SetInput(IN_COMPOSITE_IN_START + 1, aSources[0]); michael@0: } michael@0: return filter; michael@0: } michael@0: michael@0: case PrimitiveType::Merge: michael@0: { michael@0: if (aSources.Length() == 0) { michael@0: return nullptr; michael@0: } michael@0: if (aSources.Length() == 1) { michael@0: return aSources[0]; michael@0: } michael@0: RefPtr filter = aDT->CreateFilter(FilterType::COMPOSITE); michael@0: filter->SetAttribute(ATT_COMPOSITE_OPERATOR, (uint32_t)COMPOSITE_OPERATOR_OVER); michael@0: for (size_t i = 0; i < aSources.Length(); i++) { michael@0: filter->SetInput(IN_COMPOSITE_IN_START + i, aSources[i]); michael@0: } michael@0: return filter; michael@0: } michael@0: michael@0: case PrimitiveType::GaussianBlur: michael@0: { michael@0: return FilterWrappers::GaussianBlur(aDT, aSources[0], michael@0: atts.GetSize(eGaussianBlurStdDeviation)); michael@0: } michael@0: michael@0: case PrimitiveType::DropShadow: michael@0: { michael@0: RefPtr alpha = FilterWrappers::ToAlpha(aDT, aSources[0]); michael@0: RefPtr blur = FilterWrappers::GaussianBlur(aDT, alpha, michael@0: atts.GetSize(eDropShadowStdDeviation)); michael@0: RefPtr offsetBlur = FilterWrappers::Offset(aDT, blur, michael@0: atts.GetIntPoint(eDropShadowOffset)); michael@0: RefPtr flood = aDT->CreateFilter(FilterType::FLOOD); michael@0: Color color = atts.GetColor(eDropShadowColor); michael@0: if (aDescription.InputColorSpace(0) == ColorSpace::LinearRGB) { michael@0: color = Color(gsRGBToLinearRGBMap[uint8_t(color.r * 255)], michael@0: gsRGBToLinearRGBMap[uint8_t(color.g * 255)], michael@0: gsRGBToLinearRGBMap[uint8_t(color.b * 255)], michael@0: color.a); michael@0: } michael@0: flood->SetAttribute(ATT_FLOOD_COLOR, color); michael@0: michael@0: RefPtr composite = aDT->CreateFilter(FilterType::COMPOSITE); michael@0: composite->SetAttribute(ATT_COMPOSITE_OPERATOR, (uint32_t)COMPOSITE_OPERATOR_IN); michael@0: composite->SetInput(IN_COMPOSITE_IN_START, offsetBlur); michael@0: composite->SetInput(IN_COMPOSITE_IN_START + 1, flood); michael@0: michael@0: RefPtr filter = aDT->CreateFilter(FilterType::COMPOSITE); michael@0: filter->SetAttribute(ATT_COMPOSITE_OPERATOR, (uint32_t)COMPOSITE_OPERATOR_OVER); michael@0: filter->SetInput(IN_COMPOSITE_IN_START, composite); michael@0: filter->SetInput(IN_COMPOSITE_IN_START + 1, aSources[0]); michael@0: return filter; michael@0: } michael@0: michael@0: case PrimitiveType::DiffuseLighting: michael@0: case PrimitiveType::SpecularLighting: michael@0: { michael@0: bool isSpecular = michael@0: aDescription.Type() == PrimitiveType::SpecularLighting; michael@0: michael@0: AttributeMap lightAttributes = atts.GetAttributeMap(eLightingLight); michael@0: michael@0: if (lightAttributes.GetUint(eLightType) == eLightTypeNone) { michael@0: return nullptr; michael@0: } michael@0: michael@0: enum { POINT = 0, SPOT, DISTANT } lightType = POINT; michael@0: michael@0: switch (lightAttributes.GetUint(eLightType)) { michael@0: case eLightTypePoint: lightType = POINT; break; michael@0: case eLightTypeSpot: lightType = SPOT; break; michael@0: case eLightTypeDistant: lightType = DISTANT; break; michael@0: } michael@0: michael@0: static const FilterType filterType[2][DISTANT+1] = { michael@0: { FilterType::POINT_DIFFUSE, FilterType::SPOT_DIFFUSE, FilterType::DISTANT_DIFFUSE }, michael@0: { FilterType::POINT_SPECULAR, FilterType::SPOT_SPECULAR, FilterType::DISTANT_SPECULAR } michael@0: }; michael@0: RefPtr filter = michael@0: aDT->CreateFilter(filterType[isSpecular][lightType]); michael@0: michael@0: filter->SetAttribute(ATT_LIGHTING_COLOR, michael@0: atts.GetColor(eLightingColor)); michael@0: filter->SetAttribute(ATT_LIGHTING_SURFACE_SCALE, michael@0: atts.GetFloat(eLightingSurfaceScale)); michael@0: filter->SetAttribute(ATT_LIGHTING_KERNEL_UNIT_LENGTH, michael@0: atts.GetSize(eLightingKernelUnitLength)); michael@0: michael@0: if (isSpecular) { michael@0: filter->SetAttribute(ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT, michael@0: atts.GetFloat(eSpecularLightingSpecularConstant)); michael@0: filter->SetAttribute(ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT, michael@0: atts.GetFloat(eSpecularLightingSpecularExponent)); michael@0: } else { michael@0: filter->SetAttribute(ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT, michael@0: atts.GetFloat(eDiffuseLightingDiffuseConstant)); michael@0: } michael@0: michael@0: switch (lightType) { michael@0: case POINT: michael@0: filter->SetAttribute(ATT_POINT_LIGHT_POSITION, michael@0: lightAttributes.GetPoint3D(ePointLightPosition)); michael@0: break; michael@0: case SPOT: michael@0: filter->SetAttribute(ATT_SPOT_LIGHT_POSITION, michael@0: lightAttributes.GetPoint3D(eSpotLightPosition)); michael@0: filter->SetAttribute(ATT_SPOT_LIGHT_POINTS_AT, michael@0: lightAttributes.GetPoint3D(eSpotLightPointsAt)); michael@0: filter->SetAttribute(ATT_SPOT_LIGHT_FOCUS, michael@0: lightAttributes.GetFloat(eSpotLightFocus)); michael@0: filter->SetAttribute(ATT_SPOT_LIGHT_LIMITING_CONE_ANGLE, michael@0: lightAttributes.GetFloat(eSpotLightLimitingConeAngle)); michael@0: break; michael@0: case DISTANT: michael@0: filter->SetAttribute(ATT_DISTANT_LIGHT_AZIMUTH, michael@0: lightAttributes.GetFloat(eDistantLightAzimuth)); michael@0: filter->SetAttribute(ATT_DISTANT_LIGHT_ELEVATION, michael@0: lightAttributes.GetFloat(eDistantLightElevation)); michael@0: break; michael@0: } michael@0: michael@0: filter->SetInput(IN_LIGHTING_IN, aSources[0]); michael@0: michael@0: return filter; michael@0: } michael@0: michael@0: case PrimitiveType::Image: michael@0: { michael@0: Matrix TM = atts.GetMatrix(eImageTransform); michael@0: if (!TM.Determinant()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: // Pull the image from the additional image list using the index that's michael@0: // stored in the primitive description. michael@0: RefPtr inputImage = michael@0: aInputImages[atts.GetUint(eImageInputIndex)]; michael@0: michael@0: RefPtr transform = aDT->CreateFilter(FilterType::TRANSFORM); michael@0: transform->SetInput(IN_TRANSFORM_IN, inputImage); michael@0: transform->SetAttribute(ATT_TRANSFORM_MATRIX, TM); michael@0: transform->SetAttribute(ATT_TRANSFORM_FILTER, atts.GetUint(eImageFilter)); michael@0: return transform; michael@0: } michael@0: michael@0: default: michael@0: return nullptr; michael@0: } michael@0: } michael@0: michael@0: template michael@0: static const T& michael@0: ElementForIndex(int32_t aIndex, michael@0: const nsTArray& aPrimitiveElements, michael@0: const T& aSourceGraphicElement, michael@0: const T& aFillPaintElement, michael@0: const T& aStrokePaintElement) michael@0: { michael@0: switch (aIndex) { michael@0: case FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic: michael@0: case FilterPrimitiveDescription::kPrimitiveIndexSourceAlpha: michael@0: return aSourceGraphicElement; michael@0: case FilterPrimitiveDescription::kPrimitiveIndexFillPaint: michael@0: return aFillPaintElement; michael@0: case FilterPrimitiveDescription::kPrimitiveIndexStrokePaint: michael@0: return aStrokePaintElement; michael@0: default: michael@0: MOZ_ASSERT(aIndex >= 0, "bad index"); michael@0: return aPrimitiveElements[aIndex]; michael@0: } michael@0: } michael@0: michael@0: static AlphaModel michael@0: InputAlphaModelForPrimitive(const FilterPrimitiveDescription& aDescr, michael@0: int32_t aInputIndex, michael@0: AlphaModel aOriginalAlphaModel) michael@0: { michael@0: switch (aDescr.Type()) { michael@0: case PrimitiveType::Tile: michael@0: case PrimitiveType::Offset: michael@0: return aOriginalAlphaModel; michael@0: michael@0: case PrimitiveType::ColorMatrix: michael@0: case PrimitiveType::ComponentTransfer: michael@0: return AlphaModel::Unpremultiplied; michael@0: michael@0: case PrimitiveType::DisplacementMap: michael@0: return aInputIndex == 0 ? michael@0: AlphaModel::Premultiplied : AlphaModel::Unpremultiplied; michael@0: michael@0: case PrimitiveType::ConvolveMatrix: michael@0: return aDescr.Attributes().GetBool(eConvolveMatrixPreserveAlpha) ? michael@0: AlphaModel::Unpremultiplied : AlphaModel::Premultiplied; michael@0: michael@0: default: michael@0: return AlphaModel::Premultiplied; michael@0: } michael@0: } michael@0: michael@0: static AlphaModel michael@0: OutputAlphaModelForPrimitive(const FilterPrimitiveDescription& aDescr, michael@0: const nsTArray& aInputAlphaModels) michael@0: { michael@0: if (aInputAlphaModels.Length()) { michael@0: // For filters with inputs, the output is premultiplied if and only if the michael@0: // first input is premultiplied. michael@0: return InputAlphaModelForPrimitive(aDescr, 0, aInputAlphaModels[0]); michael@0: } michael@0: michael@0: // All filters without inputs produce premultiplied alpha. michael@0: return AlphaModel::Premultiplied; michael@0: } michael@0: michael@0: // Returns the output FilterNode, in premultiplied sRGB space. michael@0: static TemporaryRef michael@0: FilterNodeGraphFromDescription(DrawTarget* aDT, michael@0: const FilterDescription& aFilter, michael@0: const Rect& aResultNeededRect, michael@0: SourceSurface* aSourceGraphic, michael@0: const IntRect& aSourceGraphicRect, michael@0: SourceSurface* aFillPaint, michael@0: const IntRect& aFillPaintRect, michael@0: SourceSurface* aStrokePaint, michael@0: const IntRect& aStrokePaintRect, michael@0: nsTArray>& aAdditionalImages) michael@0: { michael@0: const nsTArray& primitives = aFilter.mPrimitives; michael@0: const IntRect& filterSpaceBounds = aFilter.mFilterSpaceBounds; michael@0: michael@0: Rect resultNeededRect(aResultNeededRect); michael@0: resultNeededRect.RoundOut(); michael@0: michael@0: RefPtr sourceFilters[4]; michael@0: nsTArray > primitiveFilters; michael@0: michael@0: for (size_t i = 0; i < primitives.Length(); ++i) { michael@0: const FilterPrimitiveDescription& descr = primitives[i]; michael@0: michael@0: nsTArray > inputFilterNodes; michael@0: nsTArray inputSourceRects; michael@0: nsTArray inputAlphaModels; michael@0: michael@0: for (size_t j = 0; j < descr.NumberOfInputs(); j++) { michael@0: michael@0: int32_t inputIndex = descr.InputPrimitiveIndex(j); michael@0: if (inputIndex < 0) { michael@0: inputSourceRects.AppendElement(filterSpaceBounds); michael@0: } else { michael@0: inputSourceRects.AppendElement(filterSpaceBounds.Intersect( michael@0: primitives[inputIndex].PrimitiveSubregion())); michael@0: } michael@0: michael@0: RefPtr inputFilter; michael@0: if (inputIndex >= 0) { michael@0: MOZ_ASSERT(inputIndex < (int64_t)primitiveFilters.Length(), "out-of-bounds input index!"); michael@0: inputFilter = primitiveFilters[inputIndex]; michael@0: MOZ_ASSERT(inputFilter, "Referred to input filter that comes after the current one?"); michael@0: } else { michael@0: int32_t sourceIndex = -inputIndex - 1; michael@0: MOZ_ASSERT(sourceIndex >= 0, "invalid source index"); michael@0: MOZ_ASSERT(sourceIndex < 4, "invalid source index"); michael@0: inputFilter = sourceFilters[sourceIndex]; michael@0: if (!inputFilter) { michael@0: RefPtr sourceFilterNode; michael@0: michael@0: nsTArray primitiveSurfaces; michael@0: nsTArray primitiveSurfaceRects; michael@0: RefPtr surf = michael@0: ElementForIndex(inputIndex, primitiveSurfaces, michael@0: aSourceGraphic, aFillPaint, aStrokePaint); michael@0: IntRect surfaceRect = michael@0: ElementForIndex(inputIndex, primitiveSurfaceRects, michael@0: aSourceGraphicRect, aFillPaintRect, aStrokePaintRect); michael@0: if (surf) { michael@0: IntPoint offset = surfaceRect.TopLeft(); michael@0: sourceFilterNode = FilterWrappers::ForSurface(aDT, surf, offset); michael@0: michael@0: if (inputIndex == FilterPrimitiveDescription::kPrimitiveIndexSourceAlpha) { michael@0: sourceFilterNode = FilterWrappers::ToAlpha(aDT, sourceFilterNode); michael@0: } michael@0: } michael@0: michael@0: inputFilter = new FilterCachedColorModels(aDT, sourceFilterNode, michael@0: ColorModel::PremulSRGB()); michael@0: sourceFilters[sourceIndex] = inputFilter; michael@0: } michael@0: } michael@0: MOZ_ASSERT(inputFilter); michael@0: michael@0: AlphaModel inputAlphaModel = michael@0: InputAlphaModelForPrimitive(descr, j, inputFilter->OriginalAlphaModel()); michael@0: inputAlphaModels.AppendElement(inputAlphaModel); michael@0: ColorModel inputColorModel(descr.InputColorSpace(j), inputAlphaModel); michael@0: inputFilterNodes.AppendElement(inputFilter->ForColorModel(inputColorModel)); michael@0: } michael@0: michael@0: RefPtr primitiveFilterNode = michael@0: FilterNodeFromPrimitiveDescription(descr, aDT, inputFilterNodes, michael@0: inputSourceRects, aAdditionalImages); michael@0: michael@0: if (primitiveFilterNode) { michael@0: IntRect cropRect = filterSpaceBounds.Intersect(descr.PrimitiveSubregion()); michael@0: primitiveFilterNode = FilterWrappers::Crop(aDT, primitiveFilterNode, cropRect); michael@0: } michael@0: michael@0: ColorModel outputColorModel(descr.OutputColorSpace(), michael@0: OutputAlphaModelForPrimitive(descr, inputAlphaModels)); michael@0: RefPtr primitiveFilter = michael@0: new FilterCachedColorModels(aDT, primitiveFilterNode, outputColorModel); michael@0: michael@0: primitiveFilters.AppendElement(primitiveFilter); michael@0: } michael@0: michael@0: return primitiveFilters.LastElement()->ForColorModel(ColorModel::PremulSRGB()); michael@0: } michael@0: michael@0: // FilterSupport michael@0: michael@0: void michael@0: FilterSupport::RenderFilterDescription(DrawTarget* aDT, michael@0: const FilterDescription& aFilter, michael@0: const Rect& aRenderRect, michael@0: SourceSurface* aSourceGraphic, michael@0: const IntRect& aSourceGraphicRect, michael@0: SourceSurface* aFillPaint, michael@0: const IntRect& aFillPaintRect, michael@0: SourceSurface* aStrokePaint, michael@0: const IntRect& aStrokePaintRect, michael@0: nsTArray>& aAdditionalImages) michael@0: { michael@0: RefPtr resultFilter = michael@0: FilterNodeGraphFromDescription(aDT, aFilter, aRenderRect, michael@0: aSourceGraphic, aSourceGraphicRect, aFillPaint, aFillPaintRect, michael@0: aStrokePaint, aStrokePaintRect, aAdditionalImages); michael@0: michael@0: aDT->DrawFilter(resultFilter, aRenderRect, Point(0, 0)); michael@0: } michael@0: michael@0: static nsIntRegion michael@0: UnionOfRegions(const nsTArray& aRegions) michael@0: { michael@0: nsIntRegion result; michael@0: for (size_t i = 0; i < aRegions.Length(); i++) { michael@0: result.Or(result, aRegions[i]); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: static int32_t michael@0: InflateSizeForBlurStdDev(float aStdDev) michael@0: { michael@0: double size = std::min(aStdDev, kMaxStdDeviation) * (3 * sqrt(2 * M_PI) / 4) * 1.5; michael@0: return uint32_t(floor(size + 0.5)); michael@0: } michael@0: michael@0: static nsIntRegion michael@0: ResultChangeRegionForPrimitive(const FilterPrimitiveDescription& aDescription, michael@0: const nsTArray& aInputChangeRegions) michael@0: { michael@0: const AttributeMap& atts = aDescription.Attributes(); michael@0: switch (aDescription.Type()) { michael@0: michael@0: case PrimitiveType::Empty: michael@0: case PrimitiveType::Flood: michael@0: case PrimitiveType::Turbulence: michael@0: case PrimitiveType::Image: michael@0: return nsIntRegion(); michael@0: michael@0: case PrimitiveType::Blend: michael@0: case PrimitiveType::Composite: michael@0: case PrimitiveType::Merge: michael@0: return UnionOfRegions(aInputChangeRegions); michael@0: michael@0: case PrimitiveType::ColorMatrix: michael@0: case PrimitiveType::ComponentTransfer: michael@0: return aInputChangeRegions[0]; michael@0: michael@0: case PrimitiveType::Morphology: michael@0: { michael@0: Size radii = atts.GetSize(eMorphologyRadii); michael@0: int32_t rx = clamped(int32_t(ceil(radii.width)), 0, kMorphologyMaxRadius); michael@0: int32_t ry = clamped(int32_t(ceil(radii.height)), 0, kMorphologyMaxRadius); michael@0: return aInputChangeRegions[0].Inflated(nsIntMargin(ry, rx, ry, rx)); michael@0: } michael@0: michael@0: case PrimitiveType::Tile: michael@0: return ThebesIntRect(aDescription.PrimitiveSubregion()); michael@0: michael@0: case PrimitiveType::ConvolveMatrix: michael@0: { michael@0: Size kernelUnitLength = atts.GetSize(eConvolveMatrixKernelUnitLength); michael@0: IntSize kernelSize = atts.GetIntSize(eConvolveMatrixKernelSize); michael@0: IntPoint target = atts.GetIntPoint(eConvolveMatrixTarget); michael@0: nsIntMargin m(ceil(kernelUnitLength.width * (target.x)), michael@0: ceil(kernelUnitLength.height * (target.y)), michael@0: ceil(kernelUnitLength.width * (kernelSize.width - target.x - 1)), michael@0: ceil(kernelUnitLength.height * (kernelSize.height - target.y - 1))); michael@0: return aInputChangeRegions[0].Inflated(m); michael@0: } michael@0: michael@0: case PrimitiveType::Offset: michael@0: { michael@0: IntPoint offset = atts.GetIntPoint(eOffsetOffset); michael@0: return aInputChangeRegions[0].MovedBy(offset.x, offset.y); michael@0: } michael@0: michael@0: case PrimitiveType::DisplacementMap: michael@0: { michael@0: int32_t scale = ceil(abs(atts.GetFloat(eDisplacementMapScale))); michael@0: return aInputChangeRegions[0].Inflated(nsIntMargin(scale, scale, scale, scale)); michael@0: } michael@0: michael@0: case PrimitiveType::GaussianBlur: michael@0: { michael@0: Size stdDeviation = atts.GetSize(eGaussianBlurStdDeviation); michael@0: int32_t dx = InflateSizeForBlurStdDev(stdDeviation.width); michael@0: int32_t dy = InflateSizeForBlurStdDev(stdDeviation.height); michael@0: return aInputChangeRegions[0].Inflated(nsIntMargin(dy, dx, dy, dx)); michael@0: } michael@0: michael@0: case PrimitiveType::DropShadow: michael@0: { michael@0: IntPoint offset = atts.GetIntPoint(eDropShadowOffset); michael@0: nsIntRegion offsetRegion = aInputChangeRegions[0].MovedBy(offset.x, offset.y); michael@0: Size stdDeviation = atts.GetSize(eDropShadowStdDeviation); michael@0: int32_t dx = InflateSizeForBlurStdDev(stdDeviation.width); michael@0: int32_t dy = InflateSizeForBlurStdDev(stdDeviation.height); michael@0: nsIntRegion blurRegion = offsetRegion.Inflated(nsIntMargin(dy, dx, dy, dx)); michael@0: blurRegion.Or(blurRegion, aInputChangeRegions[0]); michael@0: return blurRegion; michael@0: } michael@0: michael@0: case PrimitiveType::DiffuseLighting: michael@0: case PrimitiveType::SpecularLighting: michael@0: { michael@0: Size kernelUnitLength = atts.GetSize(eLightingKernelUnitLength); michael@0: int32_t dx = ceil(kernelUnitLength.width); michael@0: int32_t dy = ceil(kernelUnitLength.height); michael@0: return aInputChangeRegions[0].Inflated(nsIntMargin(dy, dx, dy, dx)); michael@0: } michael@0: michael@0: default: michael@0: return nsIntRegion(); michael@0: } michael@0: } michael@0: michael@0: /* static */ nsIntRegion michael@0: FilterSupport::ComputeResultChangeRegion(const FilterDescription& aFilter, michael@0: const nsIntRegion& aSourceGraphicChange, michael@0: const nsIntRegion& aFillPaintChange, michael@0: const nsIntRegion& aStrokePaintChange) michael@0: { michael@0: const nsTArray& primitives = aFilter.mPrimitives; michael@0: nsTArray resultChangeRegions; michael@0: michael@0: for (int32_t i = 0; i < int32_t(primitives.Length()); ++i) { michael@0: const FilterPrimitiveDescription& descr = primitives[i]; michael@0: michael@0: nsTArray inputChangeRegions; michael@0: for (size_t j = 0; j < descr.NumberOfInputs(); j++) { michael@0: int32_t inputIndex = descr.InputPrimitiveIndex(j); michael@0: MOZ_ASSERT(inputIndex < i, "bad input index"); michael@0: nsIntRegion inputChangeRegion = michael@0: ElementForIndex(inputIndex, resultChangeRegions, michael@0: aSourceGraphicChange, aFillPaintChange, michael@0: aStrokePaintChange); michael@0: inputChangeRegions.AppendElement(inputChangeRegion); michael@0: } michael@0: nsIntRegion changeRegion = michael@0: ResultChangeRegionForPrimitive(descr, inputChangeRegions); michael@0: IntRect cropRect = michael@0: descr.PrimitiveSubregion().Intersect(aFilter.mFilterSpaceBounds); michael@0: changeRegion.And(changeRegion, ThebesIntRect(cropRect)); michael@0: resultChangeRegions.AppendElement(changeRegion); michael@0: } michael@0: michael@0: return resultChangeRegions[resultChangeRegions.Length() - 1]; michael@0: } michael@0: michael@0: static nsIntRegion michael@0: PostFilterExtentsForPrimitive(const FilterPrimitiveDescription& aDescription, michael@0: const nsTArray& aInputExtents) michael@0: { michael@0: const AttributeMap& atts = aDescription.Attributes(); michael@0: switch (aDescription.Type()) { michael@0: michael@0: case PrimitiveType::Empty: michael@0: return nsIntRect(); michael@0: michael@0: case PrimitiveType::Composite: michael@0: { michael@0: uint32_t op = atts.GetUint(eCompositeOperator); michael@0: if (op == SVG_FECOMPOSITE_OPERATOR_ARITHMETIC) { michael@0: // The arithmetic composite primitive can draw outside the bounding michael@0: // box of its source images. michael@0: const nsTArray& coefficients = atts.GetFloats(eCompositeCoefficients); michael@0: MOZ_ASSERT(coefficients.Length() == 4); michael@0: michael@0: // The calculation is: michael@0: // r = c[0] * in[0] * in[1] + c[1] * in[0] + c[2] * in[1] + c[3] michael@0: nsIntRegion region; michael@0: if (coefficients[0] > 0.0f) { michael@0: region = aInputExtents[0].Intersect(aInputExtents[1]); michael@0: } michael@0: if (coefficients[1] > 0.0f) { michael@0: region.Or(region, aInputExtents[0]); michael@0: } michael@0: if (coefficients[2] > 0.0f) { michael@0: region.Or(region, aInputExtents[1]); michael@0: } michael@0: if (coefficients[3] > 0.0f) { michael@0: region = ThebesIntRect(aDescription.PrimitiveSubregion()); michael@0: } michael@0: return region; michael@0: } michael@0: if (op == SVG_FECOMPOSITE_OPERATOR_IN) { michael@0: return aInputExtents[0].Intersect(aInputExtents[1]); michael@0: } michael@0: return ResultChangeRegionForPrimitive(aDescription, aInputExtents); michael@0: } michael@0: michael@0: case PrimitiveType::Flood: michael@0: { michael@0: if (atts.GetColor(eFloodColor).a == 0.0f) { michael@0: return nsIntRect(); michael@0: } michael@0: return ThebesIntRect(aDescription.PrimitiveSubregion()); michael@0: } michael@0: michael@0: case PrimitiveType::Turbulence: michael@0: case PrimitiveType::Image: michael@0: { michael@0: return ThebesIntRect(aDescription.PrimitiveSubregion()); michael@0: } michael@0: michael@0: case PrimitiveType::Morphology: michael@0: { michael@0: uint32_t op = atts.GetUint(eMorphologyOperator); michael@0: if (op == SVG_OPERATOR_ERODE) { michael@0: return aInputExtents[0]; michael@0: } michael@0: Size radii = atts.GetSize(eMorphologyRadii); michael@0: int32_t rx = clamped(int32_t(ceil(radii.width)), 0, kMorphologyMaxRadius); michael@0: int32_t ry = clamped(int32_t(ceil(radii.height)), 0, kMorphologyMaxRadius); michael@0: return aInputExtents[0].Inflated(nsIntMargin(ry, rx, ry, rx)); michael@0: } michael@0: michael@0: default: michael@0: return ResultChangeRegionForPrimitive(aDescription, aInputExtents); michael@0: } michael@0: } michael@0: michael@0: /* static */ nsIntRegion michael@0: FilterSupport::ComputePostFilterExtents(const FilterDescription& aFilter, michael@0: const nsIntRegion& aSourceGraphicExtents) michael@0: { michael@0: const nsTArray& primitives = aFilter.mPrimitives; michael@0: nsIntRegion filterSpace = ThebesIntRect(aFilter.mFilterSpaceBounds); michael@0: nsTArray postFilterExtents; michael@0: michael@0: for (int32_t i = 0; i < int32_t(primitives.Length()); ++i) { michael@0: const FilterPrimitiveDescription& descr = primitives[i]; michael@0: michael@0: nsTArray inputExtents; michael@0: for (size_t j = 0; j < descr.NumberOfInputs(); j++) { michael@0: int32_t inputIndex = descr.InputPrimitiveIndex(j); michael@0: MOZ_ASSERT(inputIndex < i, "bad input index"); michael@0: nsIntRegion inputExtent = michael@0: ElementForIndex(inputIndex, postFilterExtents, michael@0: aSourceGraphicExtents, filterSpace, filterSpace); michael@0: inputExtents.AppendElement(inputExtent); michael@0: } michael@0: nsIntRegion extent = PostFilterExtentsForPrimitive(descr, inputExtents); michael@0: IntRect cropRect = michael@0: descr.PrimitiveSubregion().Intersect(aFilter.mFilterSpaceBounds); michael@0: extent.And(extent, ThebesIntRect(cropRect)); michael@0: postFilterExtents.AppendElement(extent); michael@0: } michael@0: michael@0: return postFilterExtents[postFilterExtents.Length() - 1]; michael@0: } michael@0: michael@0: static nsIntRegion michael@0: SourceNeededRegionForPrimitive(const FilterPrimitiveDescription& aDescription, michael@0: const nsIntRegion& aResultNeededRegion, michael@0: int32_t aInputIndex) michael@0: { michael@0: const AttributeMap& atts = aDescription.Attributes(); michael@0: switch (aDescription.Type()) { michael@0: michael@0: case PrimitiveType::Flood: michael@0: case PrimitiveType::Turbulence: michael@0: case PrimitiveType::Image: michael@0: MOZ_CRASH("this shouldn't be called for filters without inputs"); michael@0: return nsIntRegion(); michael@0: michael@0: case PrimitiveType::Empty: michael@0: return nsIntRegion(); michael@0: michael@0: case PrimitiveType::Blend: michael@0: case PrimitiveType::Composite: michael@0: case PrimitiveType::Merge: michael@0: case PrimitiveType::ColorMatrix: michael@0: case PrimitiveType::ComponentTransfer: michael@0: return aResultNeededRegion; michael@0: michael@0: case PrimitiveType::Morphology: michael@0: { michael@0: Size radii = atts.GetSize(eMorphologyRadii); michael@0: int32_t rx = clamped(int32_t(ceil(radii.width)), 0, kMorphologyMaxRadius); michael@0: int32_t ry = clamped(int32_t(ceil(radii.height)), 0, kMorphologyMaxRadius); michael@0: return aResultNeededRegion.Inflated(nsIntMargin(ry, rx, ry, rx)); michael@0: } michael@0: michael@0: case PrimitiveType::Tile: michael@0: return nsIntRect(INT32_MIN/2, INT32_MIN/2, INT32_MAX, INT32_MAX); michael@0: michael@0: case PrimitiveType::ConvolveMatrix: michael@0: { michael@0: Size kernelUnitLength = atts.GetSize(eConvolveMatrixKernelUnitLength); michael@0: IntSize kernelSize = atts.GetIntSize(eConvolveMatrixKernelSize); michael@0: IntPoint target = atts.GetIntPoint(eConvolveMatrixTarget); michael@0: nsIntMargin m(ceil(kernelUnitLength.width * (kernelSize.width - target.x - 1)), michael@0: ceil(kernelUnitLength.height * (kernelSize.height - target.y - 1)), michael@0: ceil(kernelUnitLength.width * (target.x)), michael@0: ceil(kernelUnitLength.height * (target.y))); michael@0: return aResultNeededRegion.Inflated(m); michael@0: } michael@0: michael@0: case PrimitiveType::Offset: michael@0: { michael@0: IntPoint offset = atts.GetIntPoint(eOffsetOffset); michael@0: return aResultNeededRegion.MovedBy(-nsIntPoint(offset.x, offset.y)); michael@0: } michael@0: michael@0: case PrimitiveType::DisplacementMap: michael@0: { michael@0: if (aInputIndex == 1) { michael@0: return aResultNeededRegion; michael@0: } michael@0: int32_t scale = ceil(abs(atts.GetFloat(eDisplacementMapScale))); michael@0: return aResultNeededRegion.Inflated(nsIntMargin(scale, scale, scale, scale)); michael@0: } michael@0: michael@0: case PrimitiveType::GaussianBlur: michael@0: { michael@0: Size stdDeviation = atts.GetSize(eGaussianBlurStdDeviation); michael@0: int32_t dx = InflateSizeForBlurStdDev(stdDeviation.width); michael@0: int32_t dy = InflateSizeForBlurStdDev(stdDeviation.height); michael@0: return aResultNeededRegion.Inflated(nsIntMargin(dy, dx, dy, dx)); michael@0: } michael@0: michael@0: case PrimitiveType::DropShadow: michael@0: { michael@0: IntPoint offset = atts.GetIntPoint(eDropShadowOffset); michael@0: nsIntRegion offsetRegion = michael@0: aResultNeededRegion.MovedBy(-nsIntPoint(offset.x, offset.y)); michael@0: Size stdDeviation = atts.GetSize(eDropShadowStdDeviation); michael@0: int32_t dx = InflateSizeForBlurStdDev(stdDeviation.width); michael@0: int32_t dy = InflateSizeForBlurStdDev(stdDeviation.height); michael@0: nsIntRegion blurRegion = offsetRegion.Inflated(nsIntMargin(dy, dx, dy, dx)); michael@0: blurRegion.Or(blurRegion, aResultNeededRegion); michael@0: return blurRegion; michael@0: } michael@0: michael@0: case PrimitiveType::DiffuseLighting: michael@0: case PrimitiveType::SpecularLighting: michael@0: { michael@0: Size kernelUnitLength = atts.GetSize(eLightingKernelUnitLength); michael@0: int32_t dx = ceil(kernelUnitLength.width); michael@0: int32_t dy = ceil(kernelUnitLength.height); michael@0: return aResultNeededRegion.Inflated(nsIntMargin(dy, dx, dy, dx)); michael@0: } michael@0: michael@0: default: michael@0: return nsIntRegion(); michael@0: } michael@0: michael@0: } michael@0: michael@0: /* static */ void michael@0: FilterSupport::ComputeSourceNeededRegions(const FilterDescription& aFilter, michael@0: const nsIntRegion& aResultNeededRegion, michael@0: nsIntRegion& aSourceGraphicNeededRegion, michael@0: nsIntRegion& aFillPaintNeededRegion, michael@0: nsIntRegion& aStrokePaintNeededRegion) michael@0: { michael@0: const nsTArray& primitives = aFilter.mPrimitives; michael@0: nsTArray primitiveNeededRegions; michael@0: primitiveNeededRegions.AppendElements(primitives.Length()); michael@0: michael@0: primitiveNeededRegions[primitives.Length() - 1] = aResultNeededRegion; michael@0: michael@0: for (int32_t i = primitives.Length() - 1; i >= 0; --i) { michael@0: const FilterPrimitiveDescription& descr = primitives[i]; michael@0: nsIntRegion neededRegion = primitiveNeededRegions[i]; michael@0: neededRegion.And(neededRegion, ThebesIntRect(descr.PrimitiveSubregion())); michael@0: michael@0: for (size_t j = 0; j < descr.NumberOfInputs(); j++) { michael@0: int32_t inputIndex = descr.InputPrimitiveIndex(j); michael@0: MOZ_ASSERT(inputIndex < i, "bad input index"); michael@0: nsIntRegion* inputNeededRegion = const_cast( michael@0: &ElementForIndex(inputIndex, primitiveNeededRegions, michael@0: aSourceGraphicNeededRegion, michael@0: aFillPaintNeededRegion, aStrokePaintNeededRegion)); michael@0: inputNeededRegion->Or(*inputNeededRegion, michael@0: SourceNeededRegionForPrimitive(descr, neededRegion, j)); michael@0: } michael@0: } michael@0: michael@0: aSourceGraphicNeededRegion.And(aSourceGraphicNeededRegion, michael@0: ThebesIntRect(aFilter.mFilterSpaceBounds)); michael@0: } michael@0: michael@0: // FilterPrimitiveDescription michael@0: michael@0: FilterPrimitiveDescription::FilterPrimitiveDescription() michael@0: : mType(PrimitiveType::Empty) michael@0: , mOutputColorSpace(ColorSpace::SRGB) michael@0: , mIsTainted(false) michael@0: { michael@0: } michael@0: michael@0: FilterPrimitiveDescription::FilterPrimitiveDescription(PrimitiveType aType) michael@0: : mType(aType) michael@0: , mOutputColorSpace(ColorSpace::SRGB) michael@0: , mIsTainted(false) michael@0: { michael@0: } michael@0: michael@0: FilterPrimitiveDescription::FilterPrimitiveDescription(const FilterPrimitiveDescription& aOther) michael@0: : mType(aOther.mType) michael@0: , mAttributes(aOther.mAttributes) michael@0: , mInputPrimitives(aOther.mInputPrimitives) michael@0: , mFilterPrimitiveSubregion(aOther.mFilterPrimitiveSubregion) michael@0: , mInputColorSpaces(aOther.mInputColorSpaces) michael@0: , mOutputColorSpace(aOther.mOutputColorSpace) michael@0: , mIsTainted(aOther.mIsTainted) michael@0: { michael@0: } michael@0: michael@0: FilterPrimitiveDescription& michael@0: FilterPrimitiveDescription::operator=(const FilterPrimitiveDescription& aOther) michael@0: { michael@0: if (this != &aOther) { michael@0: mType = aOther.mType; michael@0: mAttributes = aOther.mAttributes; michael@0: mInputPrimitives = aOther.mInputPrimitives; michael@0: mFilterPrimitiveSubregion = aOther.mFilterPrimitiveSubregion; michael@0: mInputColorSpaces = aOther.mInputColorSpaces; michael@0: mOutputColorSpace = aOther.mOutputColorSpace; michael@0: mIsTainted = aOther.mIsTainted; michael@0: } michael@0: return *this; michael@0: } michael@0: michael@0: bool michael@0: FilterPrimitiveDescription::operator==(const FilterPrimitiveDescription& aOther) const michael@0: { michael@0: return mType == aOther.mType && michael@0: mFilterPrimitiveSubregion.IsEqualInterior(aOther.mFilterPrimitiveSubregion) && michael@0: mOutputColorSpace == aOther.mOutputColorSpace && michael@0: mIsTainted == aOther.mIsTainted && michael@0: mInputPrimitives == aOther.mInputPrimitives && michael@0: mInputColorSpaces == aOther.mInputColorSpaces && michael@0: mAttributes == aOther.mAttributes; michael@0: } michael@0: michael@0: // FilterDescription michael@0: michael@0: bool michael@0: FilterDescription::operator==(const FilterDescription& aOther) const michael@0: { michael@0: return mFilterSpaceBounds.IsEqualInterior(aOther.mFilterSpaceBounds) && michael@0: mPrimitives == aOther.mPrimitives; michael@0: } michael@0: michael@0: // AttributeMap michael@0: michael@0: // A class that wraps different types for easy storage in a hashtable. Only michael@0: // used by AttributeMap. michael@0: struct FilterAttribute { michael@0: FilterAttribute(const FilterAttribute& aOther); michael@0: ~FilterAttribute(); michael@0: michael@0: bool operator==(const FilterAttribute& aOther) const; michael@0: bool operator!=(const FilterAttribute& aOther) const michael@0: { michael@0: return !(*this == aOther); michael@0: } michael@0: michael@0: AttributeType Type() const { return mType; } michael@0: michael@0: #define MAKE_CONSTRUCTOR_AND_ACCESSOR_BASIC(type, typeLabel) \ michael@0: FilterAttribute(type aValue) \ michael@0: : mType(AttributeType::e##typeLabel), m##typeLabel(aValue) \ michael@0: {} \ michael@0: type As##typeLabel() { \ michael@0: MOZ_ASSERT(mType == AttributeType::e##typeLabel); \ michael@0: return m##typeLabel; \ michael@0: } michael@0: michael@0: #define MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(className) \ michael@0: FilterAttribute(const className& aValue) \ michael@0: : mType(AttributeType::e##className), m##className(new className(aValue)) \ michael@0: {} \ michael@0: className As##className() { \ michael@0: MOZ_ASSERT(mType == AttributeType::e##className); \ michael@0: return *m##className; \ michael@0: } michael@0: michael@0: MAKE_CONSTRUCTOR_AND_ACCESSOR_BASIC(bool, Bool) michael@0: MAKE_CONSTRUCTOR_AND_ACCESSOR_BASIC(uint32_t, Uint) michael@0: MAKE_CONSTRUCTOR_AND_ACCESSOR_BASIC(float, Float) michael@0: MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(Size) michael@0: MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(IntSize) michael@0: MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(IntPoint) michael@0: MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(Matrix) michael@0: MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(Matrix5x4) michael@0: MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(Point3D) michael@0: MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(Color) michael@0: MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(AttributeMap) michael@0: michael@0: #undef MAKE_CONSTRUCTOR_AND_ACCESSOR_BASIC michael@0: #undef MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS michael@0: michael@0: FilterAttribute(const float* aValue, uint32_t aLength) michael@0: : mType(AttributeType::eFloats) michael@0: { michael@0: mFloats = new nsTArray(); michael@0: mFloats->AppendElements(aValue, aLength); michael@0: } michael@0: michael@0: const nsTArray& AsFloats() const { michael@0: MOZ_ASSERT(mType == AttributeType::eFloats); michael@0: return *mFloats; michael@0: } michael@0: michael@0: private: michael@0: const AttributeType mType; michael@0: michael@0: union { michael@0: bool mBool; michael@0: uint32_t mUint; michael@0: float mFloat; michael@0: Size* mSize; michael@0: IntSize* mIntSize; michael@0: IntPoint* mIntPoint; michael@0: Matrix* mMatrix; michael@0: Matrix5x4* mMatrix5x4; michael@0: Point3D* mPoint3D; michael@0: Color* mColor; michael@0: AttributeMap* mAttributeMap; michael@0: nsTArray* mFloats; michael@0: }; michael@0: }; michael@0: michael@0: FilterAttribute::FilterAttribute(const FilterAttribute& aOther) michael@0: : mType(aOther.mType) michael@0: { michael@0: switch (mType) { michael@0: case AttributeType::eBool: michael@0: mBool = aOther.mBool; michael@0: break; michael@0: case AttributeType::eUint: michael@0: mUint = aOther.mUint; michael@0: break; michael@0: case AttributeType::eFloat: michael@0: mFloat = aOther.mFloat; michael@0: break; michael@0: michael@0: #define HANDLE_CLASS(className) \ michael@0: case AttributeType::e##className: \ michael@0: m##className = new className(*aOther.m##className); \ michael@0: break; michael@0: michael@0: HANDLE_CLASS(Size) michael@0: HANDLE_CLASS(IntSize) michael@0: HANDLE_CLASS(IntPoint) michael@0: HANDLE_CLASS(Matrix) michael@0: HANDLE_CLASS(Matrix5x4) michael@0: HANDLE_CLASS(Point3D) michael@0: HANDLE_CLASS(Color) michael@0: HANDLE_CLASS(AttributeMap) michael@0: michael@0: #undef HANDLE_CLASS michael@0: michael@0: case AttributeType::eFloats: michael@0: mFloats = new nsTArray(*aOther.mFloats); michael@0: break; michael@0: case AttributeType::Max: michael@0: break; michael@0: } michael@0: } michael@0: michael@0: FilterAttribute::~FilterAttribute() { michael@0: switch (mType) { michael@0: case AttributeType::Max: michael@0: case AttributeType::eBool: michael@0: case AttributeType::eUint: michael@0: case AttributeType::eFloat: michael@0: break; michael@0: michael@0: #define HANDLE_CLASS(className) \ michael@0: case AttributeType::e##className: \ michael@0: delete m##className; \ michael@0: break; michael@0: michael@0: HANDLE_CLASS(Size) michael@0: HANDLE_CLASS(IntSize) michael@0: HANDLE_CLASS(IntPoint) michael@0: HANDLE_CLASS(Matrix) michael@0: HANDLE_CLASS(Matrix5x4) michael@0: HANDLE_CLASS(Point3D) michael@0: HANDLE_CLASS(Color) michael@0: HANDLE_CLASS(AttributeMap) michael@0: michael@0: #undef HANDLE_CLASS michael@0: michael@0: case AttributeType::eFloats: michael@0: delete mFloats; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: bool michael@0: FilterAttribute::operator==(const FilterAttribute& aOther) const michael@0: { michael@0: if (mType != aOther.mType) { michael@0: return false; michael@0: } michael@0: michael@0: switch (mType) { michael@0: michael@0: #define HANDLE_TYPE(typeName) \ michael@0: case AttributeType::e##typeName: \ michael@0: return m##typeName == aOther.m##typeName; michael@0: michael@0: HANDLE_TYPE(Bool) michael@0: HANDLE_TYPE(Uint) michael@0: HANDLE_TYPE(Float) michael@0: HANDLE_TYPE(Size) michael@0: HANDLE_TYPE(IntSize) michael@0: HANDLE_TYPE(IntPoint) michael@0: HANDLE_TYPE(Matrix) michael@0: HANDLE_TYPE(Matrix5x4) michael@0: HANDLE_TYPE(Point3D) michael@0: HANDLE_TYPE(Color) michael@0: HANDLE_TYPE(AttributeMap) michael@0: HANDLE_TYPE(Floats) michael@0: michael@0: #undef HANDLE_TYPE michael@0: michael@0: default: michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: typedef FilterAttribute Attribute; michael@0: michael@0: AttributeMap::AttributeMap() michael@0: { michael@0: } michael@0: michael@0: AttributeMap::~AttributeMap() michael@0: { michael@0: } michael@0: michael@0: static PLDHashOperator michael@0: CopyAttribute(const uint32_t& aAttributeName, michael@0: Attribute* aAttribute, michael@0: void* aAttributes) michael@0: { michael@0: typedef nsClassHashtable Map; michael@0: Map* map = static_cast(aAttributes); michael@0: map->Put(aAttributeName, new Attribute(*aAttribute)); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: AttributeMap::AttributeMap(const AttributeMap& aOther) michael@0: { michael@0: aOther.mMap.EnumerateRead(CopyAttribute, &mMap); michael@0: } michael@0: michael@0: AttributeMap& michael@0: AttributeMap::operator=(const AttributeMap& aOther) michael@0: { michael@0: if (this != &aOther) { michael@0: mMap.Clear(); michael@0: aOther.mMap.EnumerateRead(CopyAttribute, &mMap); michael@0: } michael@0: return *this; michael@0: } michael@0: michael@0: namespace { michael@0: struct MatchingMap { michael@0: typedef nsClassHashtable Map; michael@0: const Map& map; michael@0: bool matches; michael@0: }; michael@0: } michael@0: michael@0: static PLDHashOperator michael@0: CheckAttributeEquality(const uint32_t& aAttributeName, michael@0: Attribute* aAttribute, michael@0: void* aMatchingMap) michael@0: { michael@0: MatchingMap& matchingMap = *static_cast(aMatchingMap); michael@0: Attribute* matchingAttribute = matchingMap.map.Get(aAttributeName); michael@0: if (!matchingAttribute || michael@0: *matchingAttribute != *aAttribute) { michael@0: matchingMap.matches = false; michael@0: return PL_DHASH_STOP; michael@0: } michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: bool michael@0: AttributeMap::operator==(const AttributeMap& aOther) const michael@0: { michael@0: if (mMap.Count() != aOther.mMap.Count()) { michael@0: return false; michael@0: } michael@0: michael@0: MatchingMap matchingMap = { mMap, true }; michael@0: aOther.mMap.EnumerateRead(CheckAttributeEquality, &matchingMap); michael@0: return matchingMap.matches; michael@0: } michael@0: michael@0: namespace { michael@0: struct HandlerWithUserData michael@0: { michael@0: AttributeMap::AttributeHandleCallback handler; michael@0: void* userData; michael@0: }; michael@0: } michael@0: michael@0: static PLDHashOperator michael@0: PassAttributeToHandleCallback(const uint32_t& aAttributeName, michael@0: Attribute* aAttribute, michael@0: void* aHandlerWithUserData) michael@0: { michael@0: HandlerWithUserData* handlerWithUserData = michael@0: static_cast(aHandlerWithUserData); michael@0: return handlerWithUserData->handler(AttributeName(aAttributeName), michael@0: aAttribute->Type(), michael@0: handlerWithUserData->userData) ? michael@0: PL_DHASH_NEXT : PL_DHASH_STOP; michael@0: } michael@0: michael@0: void michael@0: AttributeMap::EnumerateRead(AttributeMap::AttributeHandleCallback aCallback, void* aUserData) const michael@0: { michael@0: HandlerWithUserData handlerWithUserData = { aCallback, aUserData }; michael@0: mMap.EnumerateRead(PassAttributeToHandleCallback, &handlerWithUserData); michael@0: } michael@0: michael@0: uint32_t michael@0: AttributeMap::Count() const michael@0: { michael@0: return mMap.Count(); michael@0: } michael@0: michael@0: #define MAKE_ATTRIBUTE_HANDLERS_BASIC(type, typeLabel, defaultValue) \ michael@0: type \ michael@0: AttributeMap::Get##typeLabel(AttributeName aName) const { \ michael@0: Attribute* value = mMap.Get(aName); \ michael@0: return value ? value->As##typeLabel() : defaultValue; \ michael@0: } \ michael@0: void \ michael@0: AttributeMap::Set(AttributeName aName, type aValue) { \ michael@0: mMap.Remove(aName); \ michael@0: mMap.Put(aName, new Attribute(aValue)); \ michael@0: } michael@0: michael@0: #define MAKE_ATTRIBUTE_HANDLERS_CLASS(className) \ michael@0: className \ michael@0: AttributeMap::Get##className(AttributeName aName) const { \ michael@0: Attribute* value = mMap.Get(aName); \ michael@0: return value ? value->As##className() : className(); \ michael@0: } \ michael@0: void \ michael@0: AttributeMap::Set(AttributeName aName, const className& aValue) { \ michael@0: mMap.Remove(aName); \ michael@0: mMap.Put(aName, new Attribute(aValue)); \ michael@0: } michael@0: michael@0: MAKE_ATTRIBUTE_HANDLERS_BASIC(bool, Bool, false) michael@0: MAKE_ATTRIBUTE_HANDLERS_BASIC(uint32_t, Uint, 0) michael@0: MAKE_ATTRIBUTE_HANDLERS_BASIC(float, Float, 0) michael@0: MAKE_ATTRIBUTE_HANDLERS_CLASS(Size) michael@0: MAKE_ATTRIBUTE_HANDLERS_CLASS(IntSize) michael@0: MAKE_ATTRIBUTE_HANDLERS_CLASS(IntPoint) michael@0: MAKE_ATTRIBUTE_HANDLERS_CLASS(Matrix) michael@0: MAKE_ATTRIBUTE_HANDLERS_CLASS(Matrix5x4) michael@0: MAKE_ATTRIBUTE_HANDLERS_CLASS(Point3D) michael@0: MAKE_ATTRIBUTE_HANDLERS_CLASS(Color) michael@0: MAKE_ATTRIBUTE_HANDLERS_CLASS(AttributeMap) michael@0: michael@0: #undef MAKE_ATTRIBUTE_HANDLERS_BASIC michael@0: #undef MAKE_ATTRIBUTE_HANDLERS_CLASS michael@0: michael@0: const nsTArray& michael@0: AttributeMap::GetFloats(AttributeName aName) const michael@0: { michael@0: Attribute* value = mMap.Get(aName); michael@0: if (!value) { michael@0: value = new Attribute(nullptr, 0); michael@0: mMap.Put(aName, value); michael@0: } michael@0: return value->AsFloats(); michael@0: } michael@0: michael@0: void michael@0: AttributeMap::Set(AttributeName aName, const float* aValues, int32_t aLength) michael@0: { michael@0: mMap.Remove(aName); michael@0: mMap.Put(aName, new Attribute(aValues, aLength)); michael@0: } michael@0: michael@0: } michael@0: }