gfx/src/FilterSupport.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "FilterSupport.h"
michael@0 7
michael@0 8 #include "mozilla/gfx/2D.h"
michael@0 9 #include "mozilla/gfx/Filters.h"
michael@0 10 #include "mozilla/PodOperations.h"
michael@0 11
michael@0 12 #include "gfxContext.h"
michael@0 13 #include "gfxPattern.h"
michael@0 14 #include "gfxPlatform.h"
michael@0 15 #include "gfx2DGlue.h"
michael@0 16
michael@0 17 #include "nsMargin.h"
michael@0 18
michael@0 19 // c = n / 255
michael@0 20 // c <= 0.0031308f ? c * 12.92f : 1.055f * powf(c, 1 / 2.4f) - 0.055f
michael@0 21 static const float glinearRGBTosRGBMap[256] = {
michael@0 22 0.000f, 0.050f, 0.085f, 0.111f, 0.132f, 0.150f, 0.166f, 0.181f,
michael@0 23 0.194f, 0.207f, 0.219f, 0.230f, 0.240f, 0.250f, 0.260f, 0.269f,
michael@0 24 0.278f, 0.286f, 0.295f, 0.303f, 0.310f, 0.318f, 0.325f, 0.332f,
michael@0 25 0.339f, 0.346f, 0.352f, 0.359f, 0.365f, 0.371f, 0.378f, 0.383f,
michael@0 26 0.389f, 0.395f, 0.401f, 0.406f, 0.412f, 0.417f, 0.422f, 0.427f,
michael@0 27 0.433f, 0.438f, 0.443f, 0.448f, 0.452f, 0.457f, 0.462f, 0.466f,
michael@0 28 0.471f, 0.476f, 0.480f, 0.485f, 0.489f, 0.493f, 0.498f, 0.502f,
michael@0 29 0.506f, 0.510f, 0.514f, 0.518f, 0.522f, 0.526f, 0.530f, 0.534f,
michael@0 30 0.538f, 0.542f, 0.546f, 0.549f, 0.553f, 0.557f, 0.561f, 0.564f,
michael@0 31 0.568f, 0.571f, 0.575f, 0.579f, 0.582f, 0.586f, 0.589f, 0.592f,
michael@0 32 0.596f, 0.599f, 0.603f, 0.606f, 0.609f, 0.613f, 0.616f, 0.619f,
michael@0 33 0.622f, 0.625f, 0.629f, 0.632f, 0.635f, 0.638f, 0.641f, 0.644f,
michael@0 34 0.647f, 0.650f, 0.653f, 0.656f, 0.659f, 0.662f, 0.665f, 0.668f,
michael@0 35 0.671f, 0.674f, 0.677f, 0.680f, 0.683f, 0.685f, 0.688f, 0.691f,
michael@0 36 0.694f, 0.697f, 0.699f, 0.702f, 0.705f, 0.708f, 0.710f, 0.713f,
michael@0 37 0.716f, 0.718f, 0.721f, 0.724f, 0.726f, 0.729f, 0.731f, 0.734f,
michael@0 38 0.737f, 0.739f, 0.742f, 0.744f, 0.747f, 0.749f, 0.752f, 0.754f,
michael@0 39 0.757f, 0.759f, 0.762f, 0.764f, 0.767f, 0.769f, 0.772f, 0.774f,
michael@0 40 0.776f, 0.779f, 0.781f, 0.784f, 0.786f, 0.788f, 0.791f, 0.793f,
michael@0 41 0.795f, 0.798f, 0.800f, 0.802f, 0.805f, 0.807f, 0.809f, 0.812f,
michael@0 42 0.814f, 0.816f, 0.818f, 0.821f, 0.823f, 0.825f, 0.827f, 0.829f,
michael@0 43 0.832f, 0.834f, 0.836f, 0.838f, 0.840f, 0.843f, 0.845f, 0.847f,
michael@0 44 0.849f, 0.851f, 0.853f, 0.855f, 0.857f, 0.860f, 0.862f, 0.864f,
michael@0 45 0.866f, 0.868f, 0.870f, 0.872f, 0.874f, 0.876f, 0.878f, 0.880f,
michael@0 46 0.882f, 0.884f, 0.886f, 0.888f, 0.890f, 0.892f, 0.894f, 0.896f,
michael@0 47 0.898f, 0.900f, 0.902f, 0.904f, 0.906f, 0.908f, 0.910f, 0.912f,
michael@0 48 0.914f, 0.916f, 0.918f, 0.920f, 0.922f, 0.924f, 0.926f, 0.928f,
michael@0 49 0.930f, 0.931f, 0.933f, 0.935f, 0.937f, 0.939f, 0.941f, 0.943f,
michael@0 50 0.945f, 0.946f, 0.948f, 0.950f, 0.952f, 0.954f, 0.956f, 0.957f,
michael@0 51 0.959f, 0.961f, 0.963f, 0.965f, 0.967f, 0.968f, 0.970f, 0.972f,
michael@0 52 0.974f, 0.975f, 0.977f, 0.979f, 0.981f, 0.983f, 0.984f, 0.986f,
michael@0 53 0.988f, 0.990f, 0.991f, 0.993f, 0.995f, 0.997f, 0.998f, 1.000f
michael@0 54 };
michael@0 55
michael@0 56 // c = n / 255
michael@0 57 // c <= 0.04045f ? c / 12.92f : powf((c + 0.055f) / 1.055f, 2.4f)
michael@0 58 static const float gsRGBToLinearRGBMap[256] = {
michael@0 59 0.000f, 0.000f, 0.001f, 0.001f, 0.001f, 0.002f, 0.002f, 0.002f,
michael@0 60 0.002f, 0.003f, 0.003f, 0.003f, 0.004f, 0.004f, 0.004f, 0.005f,
michael@0 61 0.005f, 0.006f, 0.006f, 0.007f, 0.007f, 0.007f, 0.008f, 0.009f,
michael@0 62 0.009f, 0.010f, 0.010f, 0.011f, 0.012f, 0.012f, 0.013f, 0.014f,
michael@0 63 0.014f, 0.015f, 0.016f, 0.017f, 0.018f, 0.019f, 0.019f, 0.020f,
michael@0 64 0.021f, 0.022f, 0.023f, 0.024f, 0.025f, 0.026f, 0.027f, 0.028f,
michael@0 65 0.030f, 0.031f, 0.032f, 0.033f, 0.034f, 0.036f, 0.037f, 0.038f,
michael@0 66 0.040f, 0.041f, 0.042f, 0.044f, 0.045f, 0.047f, 0.048f, 0.050f,
michael@0 67 0.051f, 0.053f, 0.054f, 0.056f, 0.058f, 0.060f, 0.061f, 0.063f,
michael@0 68 0.065f, 0.067f, 0.068f, 0.070f, 0.072f, 0.074f, 0.076f, 0.078f,
michael@0 69 0.080f, 0.082f, 0.084f, 0.087f, 0.089f, 0.091f, 0.093f, 0.095f,
michael@0 70 0.098f, 0.100f, 0.102f, 0.105f, 0.107f, 0.109f, 0.112f, 0.114f,
michael@0 71 0.117f, 0.120f, 0.122f, 0.125f, 0.127f, 0.130f, 0.133f, 0.136f,
michael@0 72 0.138f, 0.141f, 0.144f, 0.147f, 0.150f, 0.153f, 0.156f, 0.159f,
michael@0 73 0.162f, 0.165f, 0.168f, 0.171f, 0.175f, 0.178f, 0.181f, 0.184f,
michael@0 74 0.188f, 0.191f, 0.195f, 0.198f, 0.202f, 0.205f, 0.209f, 0.212f,
michael@0 75 0.216f, 0.220f, 0.223f, 0.227f, 0.231f, 0.235f, 0.238f, 0.242f,
michael@0 76 0.246f, 0.250f, 0.254f, 0.258f, 0.262f, 0.266f, 0.270f, 0.275f,
michael@0 77 0.279f, 0.283f, 0.287f, 0.292f, 0.296f, 0.301f, 0.305f, 0.309f,
michael@0 78 0.314f, 0.319f, 0.323f, 0.328f, 0.332f, 0.337f, 0.342f, 0.347f,
michael@0 79 0.352f, 0.356f, 0.361f, 0.366f, 0.371f, 0.376f, 0.381f, 0.386f,
michael@0 80 0.392f, 0.397f, 0.402f, 0.407f, 0.413f, 0.418f, 0.423f, 0.429f,
michael@0 81 0.434f, 0.440f, 0.445f, 0.451f, 0.456f, 0.462f, 0.468f, 0.474f,
michael@0 82 0.479f, 0.485f, 0.491f, 0.497f, 0.503f, 0.509f, 0.515f, 0.521f,
michael@0 83 0.527f, 0.533f, 0.539f, 0.546f, 0.552f, 0.558f, 0.565f, 0.571f,
michael@0 84 0.578f, 0.584f, 0.591f, 0.597f, 0.604f, 0.610f, 0.617f, 0.624f,
michael@0 85 0.631f, 0.638f, 0.644f, 0.651f, 0.658f, 0.665f, 0.672f, 0.680f,
michael@0 86 0.687f, 0.694f, 0.701f, 0.708f, 0.716f, 0.723f, 0.730f, 0.738f,
michael@0 87 0.745f, 0.753f, 0.761f, 0.768f, 0.776f, 0.784f, 0.791f, 0.799f,
michael@0 88 0.807f, 0.815f, 0.823f, 0.831f, 0.839f, 0.847f, 0.855f, 0.863f,
michael@0 89 0.871f, 0.880f, 0.888f, 0.896f, 0.905f, 0.913f, 0.922f, 0.930f,
michael@0 90 0.939f, 0.947f, 0.956f, 0.965f, 0.973f, 0.982f, 0.991f, 1.000f
michael@0 91 };
michael@0 92
michael@0 93 namespace mozilla {
michael@0 94 namespace gfx {
michael@0 95
michael@0 96 // Some convenience FilterNode creation functions.
michael@0 97
michael@0 98 static const float kMaxStdDeviation = 500;
michael@0 99
michael@0 100 namespace FilterWrappers {
michael@0 101
michael@0 102 static TemporaryRef<FilterNode>
michael@0 103 Unpremultiply(DrawTarget* aDT, FilterNode* aInput)
michael@0 104 {
michael@0 105 RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::UNPREMULTIPLY);
michael@0 106 filter->SetInput(IN_UNPREMULTIPLY_IN, aInput);
michael@0 107 return filter;
michael@0 108 }
michael@0 109
michael@0 110 static TemporaryRef<FilterNode>
michael@0 111 Premultiply(DrawTarget* aDT, FilterNode* aInput)
michael@0 112 {
michael@0 113 RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::PREMULTIPLY);
michael@0 114 filter->SetInput(IN_PREMULTIPLY_IN, aInput);
michael@0 115 return filter;
michael@0 116 }
michael@0 117
michael@0 118 static TemporaryRef<FilterNode>
michael@0 119 LinearRGBToSRGB(DrawTarget* aDT, FilterNode* aInput)
michael@0 120 {
michael@0 121 RefPtr<FilterNode> transfer = aDT->CreateFilter(FilterType::DISCRETE_TRANSFER);
michael@0 122 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_R, false);
michael@0 123 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_R, glinearRGBTosRGBMap, 256);
michael@0 124 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_G, false);
michael@0 125 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_G, glinearRGBTosRGBMap, 256);
michael@0 126 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_B, false);
michael@0 127 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_B, glinearRGBTosRGBMap, 256);
michael@0 128 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_A, true);
michael@0 129 transfer->SetInput(IN_DISCRETE_TRANSFER_IN, aInput);
michael@0 130 return transfer;
michael@0 131 }
michael@0 132
michael@0 133 static TemporaryRef<FilterNode>
michael@0 134 SRGBToLinearRGB(DrawTarget* aDT, FilterNode* aInput)
michael@0 135 {
michael@0 136 RefPtr<FilterNode> transfer = aDT->CreateFilter(FilterType::DISCRETE_TRANSFER);
michael@0 137 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_R, false);
michael@0 138 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_R, gsRGBToLinearRGBMap, 256);
michael@0 139 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_G, false);
michael@0 140 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_G, gsRGBToLinearRGBMap, 256);
michael@0 141 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_B, false);
michael@0 142 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_B, gsRGBToLinearRGBMap, 256);
michael@0 143 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_A, true);
michael@0 144 transfer->SetInput(IN_DISCRETE_TRANSFER_IN, aInput);
michael@0 145 return transfer;
michael@0 146 }
michael@0 147
michael@0 148 static TemporaryRef<FilterNode>
michael@0 149 Crop(DrawTarget* aDT, FilterNode* aInputFilter, const IntRect& aRect)
michael@0 150 {
michael@0 151 RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::CROP);
michael@0 152 filter->SetAttribute(ATT_CROP_RECT, Rect(aRect));
michael@0 153 filter->SetInput(IN_CROP_IN, aInputFilter);
michael@0 154 return filter;
michael@0 155 }
michael@0 156
michael@0 157 static TemporaryRef<FilterNode>
michael@0 158 Offset(DrawTarget* aDT, FilterNode* aInputFilter, const IntPoint& aOffset)
michael@0 159 {
michael@0 160 RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::TRANSFORM);
michael@0 161 filter->SetAttribute(ATT_TRANSFORM_MATRIX, Matrix::Translation(aOffset.x, aOffset.y));
michael@0 162 filter->SetInput(IN_TRANSFORM_IN, aInputFilter);
michael@0 163 return filter;
michael@0 164 }
michael@0 165
michael@0 166 static TemporaryRef<FilterNode>
michael@0 167 GaussianBlur(DrawTarget* aDT, FilterNode* aInputFilter, const Size& aStdDeviation)
michael@0 168 {
michael@0 169 float stdX = float(std::min(aStdDeviation.width, kMaxStdDeviation));
michael@0 170 float stdY = float(std::min(aStdDeviation.height, kMaxStdDeviation));
michael@0 171 if (stdX == stdY) {
michael@0 172 RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::GAUSSIAN_BLUR);
michael@0 173 filter->SetAttribute(ATT_GAUSSIAN_BLUR_STD_DEVIATION, stdX);
michael@0 174 filter->SetInput(IN_GAUSSIAN_BLUR_IN, aInputFilter);
michael@0 175 return filter;
michael@0 176 }
michael@0 177 RefPtr<FilterNode> filterH = aDT->CreateFilter(FilterType::DIRECTIONAL_BLUR);
michael@0 178 RefPtr<FilterNode> filterV = aDT->CreateFilter(FilterType::DIRECTIONAL_BLUR);
michael@0 179 filterH->SetAttribute(ATT_DIRECTIONAL_BLUR_DIRECTION, (uint32_t)BLUR_DIRECTION_X);
michael@0 180 filterH->SetAttribute(ATT_DIRECTIONAL_BLUR_STD_DEVIATION, stdX);
michael@0 181 filterV->SetAttribute(ATT_DIRECTIONAL_BLUR_DIRECTION, (uint32_t)BLUR_DIRECTION_Y);
michael@0 182 filterV->SetAttribute(ATT_DIRECTIONAL_BLUR_STD_DEVIATION, stdY);
michael@0 183 filterH->SetInput(IN_DIRECTIONAL_BLUR_IN, aInputFilter);
michael@0 184 filterV->SetInput(IN_DIRECTIONAL_BLUR_IN, filterH);
michael@0 185 return filterV;
michael@0 186 }
michael@0 187
michael@0 188 static TemporaryRef<FilterNode>
michael@0 189 Clear(DrawTarget* aDT)
michael@0 190 {
michael@0 191 RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::FLOOD);
michael@0 192 filter->SetAttribute(ATT_FLOOD_COLOR, Color(0,0,0,0));
michael@0 193 return filter;
michael@0 194 }
michael@0 195
michael@0 196 static TemporaryRef<FilterNode>
michael@0 197 ForSurface(DrawTarget* aDT, SourceSurface* aSurface,
michael@0 198 const IntPoint& aSurfacePosition)
michael@0 199 {
michael@0 200 RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::TRANSFORM);
michael@0 201 filter->SetAttribute(ATT_TRANSFORM_MATRIX,
michael@0 202 Matrix::Translation(aSurfacePosition.x, aSurfacePosition.y));
michael@0 203 filter->SetInput(IN_TRANSFORM_IN, aSurface);
michael@0 204 return filter;
michael@0 205 }
michael@0 206
michael@0 207 static TemporaryRef<FilterNode>
michael@0 208 ToAlpha(DrawTarget* aDT, FilterNode* aInput)
michael@0 209 {
michael@0 210 float zero = 0.0f;
michael@0 211 RefPtr<FilterNode> transfer = aDT->CreateFilter(FilterType::DISCRETE_TRANSFER);
michael@0 212 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_R, false);
michael@0 213 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_R, &zero, 1);
michael@0 214 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_G, false);
michael@0 215 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_G, &zero, 1);
michael@0 216 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_B, false);
michael@0 217 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_B, &zero, 1);
michael@0 218 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_A, true);
michael@0 219 transfer->SetInput(IN_DISCRETE_TRANSFER_IN, aInput);
michael@0 220 return transfer;
michael@0 221 }
michael@0 222
michael@0 223 }
michael@0 224
michael@0 225 // A class that wraps a FilterNode and handles conversion between different
michael@0 226 // color models. Create FilterCachedColorModels with your original filter and
michael@0 227 // the color model that this filter outputs in natively, and then call
michael@0 228 // ->ForColorModel(colorModel) in order to get a FilterNode which outputs to
michael@0 229 // the specified colorModel.
michael@0 230 // Internally, this is achieved by wrapping the original FilterNode with
michael@0 231 // conversion FilterNodes. These filter nodes are cached in such a way that no
michael@0 232 // repeated or back-and-forth conversions happen.
michael@0 233 class FilterCachedColorModels
michael@0 234 {
michael@0 235 public:
michael@0 236 NS_INLINE_DECL_REFCOUNTING(FilterCachedColorModels)
michael@0 237 // aFilter can be null. In that case, ForColorModel will return a non-null
michael@0 238 // completely transparent filter for all color models.
michael@0 239 FilterCachedColorModels(DrawTarget* aDT,
michael@0 240 FilterNode* aFilter,
michael@0 241 ColorModel aOriginalColorModel);
michael@0 242
michael@0 243 // Get a FilterNode for the specified color model, guaranteed to be non-null.
michael@0 244 TemporaryRef<FilterNode> ForColorModel(ColorModel aColorModel);
michael@0 245
michael@0 246 AlphaModel OriginalAlphaModel() const { return mOriginalColorModel.mAlphaModel; }
michael@0 247
michael@0 248 private:
michael@0 249 // Create the required FilterNode that will be cached by ForColorModel.
michael@0 250 TemporaryRef<FilterNode> WrapForColorModel(ColorModel aColorModel);
michael@0 251
michael@0 252 RefPtr<DrawTarget> mDT;
michael@0 253 ColorModel mOriginalColorModel;
michael@0 254
michael@0 255 // This array is indexed by ColorModel::ToIndex.
michael@0 256 RefPtr<FilterNode> mFilterForColorModel[4];
michael@0 257 };
michael@0 258
michael@0 259 FilterCachedColorModels::FilterCachedColorModels(DrawTarget* aDT,
michael@0 260 FilterNode* aFilter,
michael@0 261 ColorModel aOriginalColorModel)
michael@0 262 : mDT(aDT)
michael@0 263 , mOriginalColorModel(aOriginalColorModel)
michael@0 264 {
michael@0 265 if (aFilter) {
michael@0 266 mFilterForColorModel[aOriginalColorModel.ToIndex()] = aFilter;
michael@0 267 } else {
michael@0 268 RefPtr<FilterNode> clear = FilterWrappers::Clear(aDT);
michael@0 269 mFilterForColorModel[0] = clear;
michael@0 270 mFilterForColorModel[1] = clear;
michael@0 271 mFilterForColorModel[2] = clear;
michael@0 272 mFilterForColorModel[3] = clear;
michael@0 273 }
michael@0 274 }
michael@0 275
michael@0 276 TemporaryRef<FilterNode>
michael@0 277 FilterCachedColorModels::ForColorModel(ColorModel aColorModel)
michael@0 278 {
michael@0 279 if (!mFilterForColorModel[aColorModel.ToIndex()]) {
michael@0 280 mFilterForColorModel[aColorModel.ToIndex()] = WrapForColorModel(aColorModel);
michael@0 281 }
michael@0 282 return mFilterForColorModel[aColorModel.ToIndex()];
michael@0 283 }
michael@0 284
michael@0 285 TemporaryRef<FilterNode>
michael@0 286 FilterCachedColorModels::WrapForColorModel(ColorModel aColorModel)
michael@0 287 {
michael@0 288 // Convert one aspect at a time and recurse.
michael@0 289 // Conversions between premultiplied / unpremultiplied color channels for the
michael@0 290 // same color space can happen directly.
michael@0 291 // Conversions between different color spaces can only happen on
michael@0 292 // unpremultiplied color channels.
michael@0 293
michael@0 294 if (aColorModel.mAlphaModel == AlphaModel::Premultiplied) {
michael@0 295 RefPtr<FilterNode> unpre =
michael@0 296 ForColorModel(ColorModel(aColorModel.mColorSpace, AlphaModel::Unpremultiplied));
michael@0 297 return FilterWrappers::Premultiply(mDT, unpre);
michael@0 298 }
michael@0 299
michael@0 300 MOZ_ASSERT(aColorModel.mAlphaModel == AlphaModel::Unpremultiplied);
michael@0 301 if (aColorModel.mColorSpace == mOriginalColorModel.mColorSpace) {
michael@0 302 RefPtr<FilterNode> premultiplied =
michael@0 303 ForColorModel(ColorModel(aColorModel.mColorSpace, AlphaModel::Premultiplied));
michael@0 304 return FilterWrappers::Unpremultiply(mDT, premultiplied);
michael@0 305 }
michael@0 306
michael@0 307 RefPtr<FilterNode> unpremultipliedOriginal =
michael@0 308 ForColorModel(ColorModel(mOriginalColorModel.mColorSpace, AlphaModel::Unpremultiplied));
michael@0 309 if (aColorModel.mColorSpace == ColorSpace::LinearRGB) {
michael@0 310 return FilterWrappers::SRGBToLinearRGB(mDT, unpremultipliedOriginal);
michael@0 311 }
michael@0 312 return FilterWrappers::LinearRGBToSRGB(mDT, unpremultipliedOriginal);
michael@0 313 }
michael@0 314
michael@0 315 // Create a 4x5 color matrix for the different ways to specify color matrices
michael@0 316 // in SVG.
michael@0 317 static nsresult
michael@0 318 ComputeColorMatrix(uint32_t aColorMatrixType, const nsTArray<float>& aValues,
michael@0 319 float aOutMatrix[20])
michael@0 320 {
michael@0 321 static const float identityMatrix[] =
michael@0 322 { 1, 0, 0, 0, 0,
michael@0 323 0, 1, 0, 0, 0,
michael@0 324 0, 0, 1, 0, 0,
michael@0 325 0, 0, 0, 1, 0 };
michael@0 326
michael@0 327 static const float luminanceToAlphaMatrix[] =
michael@0 328 { 0, 0, 0, 0, 0,
michael@0 329 0, 0, 0, 0, 0,
michael@0 330 0, 0, 0, 0, 0,
michael@0 331 0.2125f, 0.7154f, 0.0721f, 0, 0 };
michael@0 332
michael@0 333 switch (aColorMatrixType) {
michael@0 334
michael@0 335 case SVG_FECOLORMATRIX_TYPE_MATRIX:
michael@0 336 {
michael@0 337 if (aValues.Length() != 20)
michael@0 338 return NS_ERROR_FAILURE;
michael@0 339
michael@0 340 PodCopy(aOutMatrix, aValues.Elements(), 20);
michael@0 341 break;
michael@0 342 }
michael@0 343
michael@0 344 case SVG_FECOLORMATRIX_TYPE_SATURATE:
michael@0 345 {
michael@0 346 if (aValues.Length() != 1)
michael@0 347 return NS_ERROR_FAILURE;
michael@0 348
michael@0 349 float s = aValues[0];
michael@0 350
michael@0 351 if (s < 0)
michael@0 352 return NS_ERROR_FAILURE;
michael@0 353
michael@0 354 PodCopy(aOutMatrix, identityMatrix, 20);
michael@0 355
michael@0 356 aOutMatrix[0] = 0.213f + 0.787f * s;
michael@0 357 aOutMatrix[1] = 0.715f - 0.715f * s;
michael@0 358 aOutMatrix[2] = 0.072f - 0.072f * s;
michael@0 359
michael@0 360 aOutMatrix[5] = 0.213f - 0.213f * s;
michael@0 361 aOutMatrix[6] = 0.715f + 0.285f * s;
michael@0 362 aOutMatrix[7] = 0.072f - 0.072f * s;
michael@0 363
michael@0 364 aOutMatrix[10] = 0.213f - 0.213f * s;
michael@0 365 aOutMatrix[11] = 0.715f - 0.715f * s;
michael@0 366 aOutMatrix[12] = 0.072f + 0.928f * s;
michael@0 367
michael@0 368 break;
michael@0 369 }
michael@0 370
michael@0 371 case SVG_FECOLORMATRIX_TYPE_HUE_ROTATE:
michael@0 372 {
michael@0 373 if (aValues.Length() != 1)
michael@0 374 return NS_ERROR_FAILURE;
michael@0 375
michael@0 376 PodCopy(aOutMatrix, identityMatrix, 20);
michael@0 377
michael@0 378 float hueRotateValue = aValues[0];
michael@0 379
michael@0 380 float c = static_cast<float>(cos(hueRotateValue * M_PI / 180));
michael@0 381 float s = static_cast<float>(sin(hueRotateValue * M_PI / 180));
michael@0 382
michael@0 383 aOutMatrix[0] = 0.213f + 0.787f * c - 0.213f * s;
michael@0 384 aOutMatrix[1] = 0.715f - 0.715f * c - 0.715f * s;
michael@0 385 aOutMatrix[2] = 0.072f - 0.072f * c + 0.928f * s;
michael@0 386
michael@0 387 aOutMatrix[5] = 0.213f - 0.213f * c + 0.143f * s;
michael@0 388 aOutMatrix[6] = 0.715f + 0.285f * c + 0.140f * s;
michael@0 389 aOutMatrix[7] = 0.072f - 0.072f * c - 0.283f * s;
michael@0 390
michael@0 391 aOutMatrix[10] = 0.213f - 0.213f * c - 0.787f * s;
michael@0 392 aOutMatrix[11] = 0.715f - 0.715f * c + 0.715f * s;
michael@0 393 aOutMatrix[12] = 0.072f + 0.928f * c + 0.072f * s;
michael@0 394
michael@0 395 break;
michael@0 396 }
michael@0 397
michael@0 398 case SVG_FECOLORMATRIX_TYPE_LUMINANCE_TO_ALPHA:
michael@0 399 {
michael@0 400 PodCopy(aOutMatrix, luminanceToAlphaMatrix, 20);
michael@0 401 break;
michael@0 402 }
michael@0 403
michael@0 404 default:
michael@0 405 return NS_ERROR_FAILURE;
michael@0 406
michael@0 407 }
michael@0 408
michael@0 409 return NS_OK;
michael@0 410 }
michael@0 411
michael@0 412 static void
michael@0 413 DisableAllTransfers(FilterNode* aTransferFilterNode)
michael@0 414 {
michael@0 415 aTransferFilterNode->SetAttribute(ATT_TRANSFER_DISABLE_R, true);
michael@0 416 aTransferFilterNode->SetAttribute(ATT_TRANSFER_DISABLE_G, true);
michael@0 417 aTransferFilterNode->SetAttribute(ATT_TRANSFER_DISABLE_B, true);
michael@0 418 aTransferFilterNode->SetAttribute(ATT_TRANSFER_DISABLE_A, true);
michael@0 419 }
michael@0 420
michael@0 421 // Called for one channel at a time.
michael@0 422 // This function creates the required FilterNodes on demand and tries to
michael@0 423 // merge conversions of different channels into the same FilterNode if
michael@0 424 // possible.
michael@0 425 // There's a mismatch between the way SVG and the Moz2D API handle transfer
michael@0 426 // functions: In SVG, it's possible to specify a different transfer function
michael@0 427 // type for each color channel, but in Moz2D, a given transfer function type
michael@0 428 // applies to all color channels.
michael@0 429 //
michael@0 430 // @param aFunctionAttributes The attributes of the transfer function for this
michael@0 431 // channel.
michael@0 432 // @param aChannel The color channel that this function applies to, where
michael@0 433 // 0 = red, 1 = green, 2 = blue, 3 = alpha
michael@0 434 // @param aDT The DrawTarget that the FilterNodes should be created for.
michael@0 435 // @param aTableTransfer Existing FilterNode holders (which may still be
michael@0 436 // null) that the resulting FilterNodes from this
michael@0 437 // function will be stored in.
michael@0 438 //
michael@0 439 static void
michael@0 440 ConvertComponentTransferFunctionToFilter(const AttributeMap& aFunctionAttributes,
michael@0 441 int32_t aChannel,
michael@0 442 DrawTarget* aDT,
michael@0 443 RefPtr<FilterNode>& aTableTransfer,
michael@0 444 RefPtr<FilterNode>& aDiscreteTransfer,
michael@0 445 RefPtr<FilterNode>& aLinearTransfer,
michael@0 446 RefPtr<FilterNode>& aGammaTransfer)
michael@0 447 {
michael@0 448 static const TransferAtts disableAtt[4] = {
michael@0 449 ATT_TRANSFER_DISABLE_R,
michael@0 450 ATT_TRANSFER_DISABLE_G,
michael@0 451 ATT_TRANSFER_DISABLE_B,
michael@0 452 ATT_TRANSFER_DISABLE_A
michael@0 453 };
michael@0 454
michael@0 455 RefPtr<FilterNode> filter;
michael@0 456
michael@0 457 uint32_t type = aFunctionAttributes.GetUint(eComponentTransferFunctionType);
michael@0 458
michael@0 459 switch (type) {
michael@0 460 case SVG_FECOMPONENTTRANSFER_TYPE_TABLE:
michael@0 461 {
michael@0 462 const nsTArray<float>& tableValues =
michael@0 463 aFunctionAttributes.GetFloats(eComponentTransferFunctionTableValues);
michael@0 464 if (tableValues.Length() < 2)
michael@0 465 return;
michael@0 466
michael@0 467 if (!aTableTransfer) {
michael@0 468 aTableTransfer = aDT->CreateFilter(FilterType::TABLE_TRANSFER);
michael@0 469 DisableAllTransfers(aTableTransfer);
michael@0 470 }
michael@0 471 filter = aTableTransfer;
michael@0 472 static const TableTransferAtts tableAtt[4] = {
michael@0 473 ATT_TABLE_TRANSFER_TABLE_R,
michael@0 474 ATT_TABLE_TRANSFER_TABLE_G,
michael@0 475 ATT_TABLE_TRANSFER_TABLE_B,
michael@0 476 ATT_TABLE_TRANSFER_TABLE_A
michael@0 477 };
michael@0 478 filter->SetAttribute(disableAtt[aChannel], false);
michael@0 479 filter->SetAttribute(tableAtt[aChannel], &tableValues[0], tableValues.Length());
michael@0 480 break;
michael@0 481 }
michael@0 482
michael@0 483 case SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE:
michael@0 484 {
michael@0 485 const nsTArray<float>& tableValues =
michael@0 486 aFunctionAttributes.GetFloats(eComponentTransferFunctionTableValues);
michael@0 487 if (tableValues.Length() < 1)
michael@0 488 return;
michael@0 489
michael@0 490 if (!aDiscreteTransfer) {
michael@0 491 aDiscreteTransfer = aDT->CreateFilter(FilterType::DISCRETE_TRANSFER);
michael@0 492 DisableAllTransfers(aDiscreteTransfer);
michael@0 493 }
michael@0 494 filter = aDiscreteTransfer;
michael@0 495 static const DiscreteTransferAtts tableAtt[4] = {
michael@0 496 ATT_DISCRETE_TRANSFER_TABLE_R,
michael@0 497 ATT_DISCRETE_TRANSFER_TABLE_G,
michael@0 498 ATT_DISCRETE_TRANSFER_TABLE_B,
michael@0 499 ATT_DISCRETE_TRANSFER_TABLE_A
michael@0 500 };
michael@0 501 filter->SetAttribute(disableAtt[aChannel], false);
michael@0 502 filter->SetAttribute(tableAtt[aChannel], &tableValues[0], tableValues.Length());
michael@0 503
michael@0 504 break;
michael@0 505 }
michael@0 506
michael@0 507 case SVG_FECOMPONENTTRANSFER_TYPE_LINEAR:
michael@0 508 {
michael@0 509 static const LinearTransferAtts slopeAtt[4] = {
michael@0 510 ATT_LINEAR_TRANSFER_SLOPE_R,
michael@0 511 ATT_LINEAR_TRANSFER_SLOPE_G,
michael@0 512 ATT_LINEAR_TRANSFER_SLOPE_B,
michael@0 513 ATT_LINEAR_TRANSFER_SLOPE_A
michael@0 514 };
michael@0 515 static const LinearTransferAtts interceptAtt[4] = {
michael@0 516 ATT_LINEAR_TRANSFER_INTERCEPT_R,
michael@0 517 ATT_LINEAR_TRANSFER_INTERCEPT_G,
michael@0 518 ATT_LINEAR_TRANSFER_INTERCEPT_B,
michael@0 519 ATT_LINEAR_TRANSFER_INTERCEPT_A
michael@0 520 };
michael@0 521 if (!aLinearTransfer) {
michael@0 522 aLinearTransfer = aDT->CreateFilter(FilterType::LINEAR_TRANSFER);
michael@0 523 DisableAllTransfers(aLinearTransfer);
michael@0 524 }
michael@0 525 filter = aLinearTransfer;
michael@0 526 filter->SetAttribute(disableAtt[aChannel], false);
michael@0 527 float slope = aFunctionAttributes.GetFloat(eComponentTransferFunctionSlope);
michael@0 528 float intercept = aFunctionAttributes.GetFloat(eComponentTransferFunctionIntercept);
michael@0 529 filter->SetAttribute(slopeAtt[aChannel], slope);
michael@0 530 filter->SetAttribute(interceptAtt[aChannel], intercept);
michael@0 531 break;
michael@0 532 }
michael@0 533
michael@0 534 case SVG_FECOMPONENTTRANSFER_TYPE_GAMMA:
michael@0 535 {
michael@0 536 static const GammaTransferAtts amplitudeAtt[4] = {
michael@0 537 ATT_GAMMA_TRANSFER_AMPLITUDE_R,
michael@0 538 ATT_GAMMA_TRANSFER_AMPLITUDE_G,
michael@0 539 ATT_GAMMA_TRANSFER_AMPLITUDE_B,
michael@0 540 ATT_GAMMA_TRANSFER_AMPLITUDE_A
michael@0 541 };
michael@0 542 static const GammaTransferAtts exponentAtt[4] = {
michael@0 543 ATT_GAMMA_TRANSFER_EXPONENT_R,
michael@0 544 ATT_GAMMA_TRANSFER_EXPONENT_G,
michael@0 545 ATT_GAMMA_TRANSFER_EXPONENT_B,
michael@0 546 ATT_GAMMA_TRANSFER_EXPONENT_A
michael@0 547 };
michael@0 548 static const GammaTransferAtts offsetAtt[4] = {
michael@0 549 ATT_GAMMA_TRANSFER_OFFSET_R,
michael@0 550 ATT_GAMMA_TRANSFER_OFFSET_G,
michael@0 551 ATT_GAMMA_TRANSFER_OFFSET_B,
michael@0 552 ATT_GAMMA_TRANSFER_OFFSET_A
michael@0 553 };
michael@0 554 if (!aGammaTransfer) {
michael@0 555 aGammaTransfer = aDT->CreateFilter(FilterType::GAMMA_TRANSFER);
michael@0 556 DisableAllTransfers(aGammaTransfer);
michael@0 557 }
michael@0 558 filter = aGammaTransfer;
michael@0 559 filter->SetAttribute(disableAtt[aChannel], false);
michael@0 560 float amplitude = aFunctionAttributes.GetFloat(eComponentTransferFunctionAmplitude);
michael@0 561 float exponent = aFunctionAttributes.GetFloat(eComponentTransferFunctionExponent);
michael@0 562 float offset = aFunctionAttributes.GetFloat(eComponentTransferFunctionOffset);
michael@0 563 filter->SetAttribute(amplitudeAtt[aChannel], amplitude);
michael@0 564 filter->SetAttribute(exponentAtt[aChannel], exponent);
michael@0 565 filter->SetAttribute(offsetAtt[aChannel], offset);
michael@0 566 break;
michael@0 567 }
michael@0 568
michael@0 569 case SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY:
michael@0 570 default:
michael@0 571 break;
michael@0 572 }
michael@0 573 }
michael@0 574
michael@0 575 const int32_t kMorphologyMaxRadius = 100000;
michael@0 576
michael@0 577 // Handle the different primitive description types and create the necessary
michael@0 578 // FilterNode(s) for each.
michael@0 579 // Returns nullptr for invalid filter primitives. This should be interpreted as
michael@0 580 // transparent black by the caller.
michael@0 581 // aSourceRegions contains the filter primitive subregions of the source
michael@0 582 // primitives; only needed for eTile primitives.
michael@0 583 // aInputImages carries additional surfaces that are used by eImage primitives.
michael@0 584 static TemporaryRef<FilterNode>
michael@0 585 FilterNodeFromPrimitiveDescription(const FilterPrimitiveDescription& aDescription,
michael@0 586 DrawTarget* aDT,
michael@0 587 nsTArray<RefPtr<FilterNode> >& aSources,
michael@0 588 nsTArray<IntRect>& aSourceRegions,
michael@0 589 nsTArray<RefPtr<SourceSurface>>& aInputImages)
michael@0 590 {
michael@0 591 const AttributeMap& atts = aDescription.Attributes();
michael@0 592 switch (aDescription.Type()) {
michael@0 593
michael@0 594 case PrimitiveType::Empty:
michael@0 595 return nullptr;
michael@0 596
michael@0 597 case PrimitiveType::Blend:
michael@0 598 {
michael@0 599 uint32_t mode = atts.GetUint(eBlendBlendmode);
michael@0 600 RefPtr<FilterNode> filter;
michael@0 601 if (mode == SVG_FEBLEND_MODE_UNKNOWN) {
michael@0 602 return nullptr;
michael@0 603 }
michael@0 604 if (mode == SVG_FEBLEND_MODE_NORMAL) {
michael@0 605 filter = aDT->CreateFilter(FilterType::COMPOSITE);
michael@0 606 filter->SetInput(IN_COMPOSITE_IN_START, aSources[1]);
michael@0 607 filter->SetInput(IN_COMPOSITE_IN_START + 1, aSources[0]);
michael@0 608 } else {
michael@0 609 filter = aDT->CreateFilter(FilterType::BLEND);
michael@0 610 static const uint8_t blendModes[SVG_FEBLEND_MODE_LIGHTEN + 1] = {
michael@0 611 0,
michael@0 612 0,
michael@0 613 BLEND_MODE_MULTIPLY,
michael@0 614 BLEND_MODE_SCREEN,
michael@0 615 BLEND_MODE_DARKEN,
michael@0 616 BLEND_MODE_LIGHTEN
michael@0 617 };
michael@0 618 filter->SetAttribute(ATT_BLEND_BLENDMODE, (uint32_t)blendModes[mode]);
michael@0 619 filter->SetInput(IN_BLEND_IN, aSources[0]);
michael@0 620 filter->SetInput(IN_BLEND_IN2, aSources[1]);
michael@0 621 }
michael@0 622 return filter;
michael@0 623 }
michael@0 624
michael@0 625 case PrimitiveType::ColorMatrix:
michael@0 626 {
michael@0 627 float colorMatrix[20];
michael@0 628 uint32_t type = atts.GetUint(eColorMatrixType);
michael@0 629 const nsTArray<float>& values = atts.GetFloats(eColorMatrixValues);
michael@0 630 if (NS_FAILED(ComputeColorMatrix(type, values, colorMatrix))) {
michael@0 631 return aSources[0];
michael@0 632 }
michael@0 633 Matrix5x4 matrix(colorMatrix[0], colorMatrix[5], colorMatrix[10], colorMatrix[15],
michael@0 634 colorMatrix[1], colorMatrix[6], colorMatrix[11], colorMatrix[16],
michael@0 635 colorMatrix[2], colorMatrix[7], colorMatrix[12], colorMatrix[17],
michael@0 636 colorMatrix[3], colorMatrix[8], colorMatrix[13], colorMatrix[18],
michael@0 637 colorMatrix[4], colorMatrix[9], colorMatrix[14], colorMatrix[19]);
michael@0 638 RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::COLOR_MATRIX);
michael@0 639 filter->SetAttribute(ATT_COLOR_MATRIX_MATRIX, matrix);
michael@0 640 filter->SetAttribute(ATT_COLOR_MATRIX_ALPHA_MODE, (uint32_t)ALPHA_MODE_STRAIGHT);
michael@0 641 filter->SetInput(IN_COLOR_MATRIX_IN, aSources[0]);
michael@0 642 return filter;
michael@0 643 }
michael@0 644
michael@0 645 case PrimitiveType::Morphology:
michael@0 646 {
michael@0 647 Size radii = atts.GetSize(eMorphologyRadii);
michael@0 648 int32_t rx = radii.width;
michael@0 649 int32_t ry = radii.height;
michael@0 650 if (rx < 0 || ry < 0) {
michael@0 651 // XXX SVGContentUtils::ReportToConsole()
michael@0 652 return nullptr;
michael@0 653 }
michael@0 654 if (rx == 0 && ry == 0) {
michael@0 655 return nullptr;
michael@0 656 }
michael@0 657
michael@0 658 // Clamp radii to prevent completely insane values:
michael@0 659 rx = std::min(rx, kMorphologyMaxRadius);
michael@0 660 ry = std::min(ry, kMorphologyMaxRadius);
michael@0 661
michael@0 662 MorphologyOperator op = atts.GetUint(eMorphologyOperator) == SVG_OPERATOR_ERODE ?
michael@0 663 MORPHOLOGY_OPERATOR_ERODE : MORPHOLOGY_OPERATOR_DILATE;
michael@0 664
michael@0 665 RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::MORPHOLOGY);
michael@0 666 filter->SetAttribute(ATT_MORPHOLOGY_RADII, IntSize(rx, ry));
michael@0 667 filter->SetAttribute(ATT_MORPHOLOGY_OPERATOR, (uint32_t)op);
michael@0 668 filter->SetInput(IN_MORPHOLOGY_IN, aSources[0]);
michael@0 669 return filter;
michael@0 670 }
michael@0 671
michael@0 672 case PrimitiveType::Flood:
michael@0 673 {
michael@0 674 Color color = atts.GetColor(eFloodColor);
michael@0 675 RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::FLOOD);
michael@0 676 filter->SetAttribute(ATT_FLOOD_COLOR, color);
michael@0 677 return filter;
michael@0 678 }
michael@0 679
michael@0 680 case PrimitiveType::Tile:
michael@0 681 {
michael@0 682 RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::TILE);
michael@0 683 filter->SetAttribute(ATT_TILE_SOURCE_RECT, aSourceRegions[0]);
michael@0 684 filter->SetInput(IN_TILE_IN, aSources[0]);
michael@0 685 return filter;
michael@0 686 }
michael@0 687
michael@0 688 case PrimitiveType::ComponentTransfer:
michael@0 689 {
michael@0 690 RefPtr<FilterNode> filters[4]; // one for each FILTER_*_TRANSFER type
michael@0 691 static const AttributeName componentFunctionNames[4] = {
michael@0 692 eComponentTransferFunctionR,
michael@0 693 eComponentTransferFunctionG,
michael@0 694 eComponentTransferFunctionB,
michael@0 695 eComponentTransferFunctionA
michael@0 696 };
michael@0 697 for (int32_t i = 0; i < 4; i++) {
michael@0 698 AttributeMap functionAttributes =
michael@0 699 atts.GetAttributeMap(componentFunctionNames[i]);
michael@0 700 ConvertComponentTransferFunctionToFilter(functionAttributes, i, aDT,
michael@0 701 filters[0], filters[1], filters[2], filters[3]);
michael@0 702 }
michael@0 703
michael@0 704 // Connect all used filters nodes.
michael@0 705 RefPtr<FilterNode> lastFilter = aSources[0];
michael@0 706 for (int32_t i = 0; i < 4; i++) {
michael@0 707 if (filters[i]) {
michael@0 708 filters[i]->SetInput(0, lastFilter);
michael@0 709 lastFilter = filters[i];
michael@0 710 }
michael@0 711 }
michael@0 712
michael@0 713 return lastFilter;
michael@0 714 }
michael@0 715
michael@0 716 case PrimitiveType::ConvolveMatrix:
michael@0 717 {
michael@0 718 RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::CONVOLVE_MATRIX);
michael@0 719 filter->SetAttribute(ATT_CONVOLVE_MATRIX_KERNEL_SIZE, atts.GetIntSize(eConvolveMatrixKernelSize));
michael@0 720 const nsTArray<float>& matrix = atts.GetFloats(eConvolveMatrixKernelMatrix);
michael@0 721 filter->SetAttribute(ATT_CONVOLVE_MATRIX_KERNEL_MATRIX,
michael@0 722 matrix.Elements(), matrix.Length());
michael@0 723 filter->SetAttribute(ATT_CONVOLVE_MATRIX_DIVISOR,
michael@0 724 atts.GetFloat(eConvolveMatrixDivisor));
michael@0 725 filter->SetAttribute(ATT_CONVOLVE_MATRIX_BIAS,
michael@0 726 atts.GetFloat(eConvolveMatrixBias));
michael@0 727 filter->SetAttribute(ATT_CONVOLVE_MATRIX_TARGET,
michael@0 728 atts.GetIntPoint(eConvolveMatrixTarget));
michael@0 729 filter->SetAttribute(ATT_CONVOLVE_MATRIX_SOURCE_RECT,
michael@0 730 aSourceRegions[0]);
michael@0 731 uint32_t edgeMode = atts.GetUint(eConvolveMatrixEdgeMode);
michael@0 732 static const uint8_t edgeModes[SVG_EDGEMODE_NONE+1] = {
michael@0 733 EDGE_MODE_NONE, // SVG_EDGEMODE_UNKNOWN
michael@0 734 EDGE_MODE_DUPLICATE, // SVG_EDGEMODE_DUPLICATE
michael@0 735 EDGE_MODE_WRAP, // SVG_EDGEMODE_WRAP
michael@0 736 EDGE_MODE_NONE // SVG_EDGEMODE_NONE
michael@0 737 };
michael@0 738 filter->SetAttribute(ATT_CONVOLVE_MATRIX_EDGE_MODE, (uint32_t)edgeModes[edgeMode]);
michael@0 739 filter->SetAttribute(ATT_CONVOLVE_MATRIX_KERNEL_UNIT_LENGTH,
michael@0 740 atts.GetSize(eConvolveMatrixKernelUnitLength));
michael@0 741 filter->SetAttribute(ATT_CONVOLVE_MATRIX_PRESERVE_ALPHA,
michael@0 742 atts.GetBool(eConvolveMatrixPreserveAlpha));
michael@0 743 filter->SetInput(IN_CONVOLVE_MATRIX_IN, aSources[0]);
michael@0 744 return filter;
michael@0 745 }
michael@0 746
michael@0 747 case PrimitiveType::Offset:
michael@0 748 {
michael@0 749 return FilterWrappers::Offset(aDT, aSources[0],
michael@0 750 atts.GetIntPoint(eOffsetOffset));
michael@0 751 }
michael@0 752
michael@0 753 case PrimitiveType::DisplacementMap:
michael@0 754 {
michael@0 755 RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::DISPLACEMENT_MAP);
michael@0 756 filter->SetAttribute(ATT_DISPLACEMENT_MAP_SCALE,
michael@0 757 atts.GetFloat(eDisplacementMapScale));
michael@0 758 static const uint8_t channel[SVG_CHANNEL_A+1] = {
michael@0 759 COLOR_CHANNEL_R, // SVG_CHANNEL_UNKNOWN
michael@0 760 COLOR_CHANNEL_R, // SVG_CHANNEL_R
michael@0 761 COLOR_CHANNEL_G, // SVG_CHANNEL_G
michael@0 762 COLOR_CHANNEL_B, // SVG_CHANNEL_B
michael@0 763 COLOR_CHANNEL_A // SVG_CHANNEL_A
michael@0 764 };
michael@0 765 filter->SetAttribute(ATT_DISPLACEMENT_MAP_X_CHANNEL,
michael@0 766 (uint32_t)channel[atts.GetUint(eDisplacementMapXChannel)]);
michael@0 767 filter->SetAttribute(ATT_DISPLACEMENT_MAP_Y_CHANNEL,
michael@0 768 (uint32_t)channel[atts.GetUint(eDisplacementMapYChannel)]);
michael@0 769 filter->SetInput(IN_DISPLACEMENT_MAP_IN, aSources[0]);
michael@0 770 filter->SetInput(IN_DISPLACEMENT_MAP_IN2, aSources[1]);
michael@0 771 return filter;
michael@0 772 }
michael@0 773
michael@0 774 case PrimitiveType::Turbulence:
michael@0 775 {
michael@0 776 RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::TURBULENCE);
michael@0 777 filter->SetAttribute(ATT_TURBULENCE_BASE_FREQUENCY,
michael@0 778 atts.GetSize(eTurbulenceBaseFrequency));
michael@0 779 filter->SetAttribute(ATT_TURBULENCE_NUM_OCTAVES,
michael@0 780 atts.GetUint(eTurbulenceNumOctaves));
michael@0 781 filter->SetAttribute(ATT_TURBULENCE_STITCHABLE,
michael@0 782 atts.GetBool(eTurbulenceStitchable));
michael@0 783 filter->SetAttribute(ATT_TURBULENCE_SEED,
michael@0 784 (uint32_t)atts.GetFloat(eTurbulenceSeed));
michael@0 785 static const uint8_t type[SVG_TURBULENCE_TYPE_TURBULENCE+1] = {
michael@0 786 TURBULENCE_TYPE_FRACTAL_NOISE, // SVG_TURBULENCE_TYPE_UNKNOWN
michael@0 787 TURBULENCE_TYPE_FRACTAL_NOISE, // SVG_TURBULENCE_TYPE_FRACTALNOISE
michael@0 788 TURBULENCE_TYPE_TURBULENCE // SVG_TURBULENCE_TYPE_TURBULENCE
michael@0 789 };
michael@0 790 filter->SetAttribute(ATT_TURBULENCE_TYPE,
michael@0 791 (uint32_t)type[atts.GetUint(eTurbulenceType)]);
michael@0 792 filter->SetAttribute(ATT_TURBULENCE_RECT,
michael@0 793 aDescription.PrimitiveSubregion() - atts.GetIntPoint(eTurbulenceOffset));
michael@0 794 return FilterWrappers::Offset(aDT, filter, atts.GetIntPoint(eTurbulenceOffset));
michael@0 795 }
michael@0 796
michael@0 797 case PrimitiveType::Composite:
michael@0 798 {
michael@0 799 RefPtr<FilterNode> filter;
michael@0 800 uint32_t op = atts.GetUint(eCompositeOperator);
michael@0 801 if (op == SVG_FECOMPOSITE_OPERATOR_ARITHMETIC) {
michael@0 802 filter = aDT->CreateFilter(FilterType::ARITHMETIC_COMBINE);
michael@0 803 const nsTArray<float>& coefficients = atts.GetFloats(eCompositeCoefficients);
michael@0 804 filter->SetAttribute(ATT_ARITHMETIC_COMBINE_COEFFICIENTS,
michael@0 805 coefficients.Elements(), coefficients.Length());
michael@0 806 filter->SetInput(IN_ARITHMETIC_COMBINE_IN, aSources[0]);
michael@0 807 filter->SetInput(IN_ARITHMETIC_COMBINE_IN2, aSources[1]);
michael@0 808 } else {
michael@0 809 filter = aDT->CreateFilter(FilterType::COMPOSITE);
michael@0 810 static const uint8_t operators[SVG_FECOMPOSITE_OPERATOR_ARITHMETIC] = {
michael@0 811 COMPOSITE_OPERATOR_OVER, // SVG_FECOMPOSITE_OPERATOR_UNKNOWN
michael@0 812 COMPOSITE_OPERATOR_OVER, // SVG_FECOMPOSITE_OPERATOR_OVER
michael@0 813 COMPOSITE_OPERATOR_IN, // SVG_FECOMPOSITE_OPERATOR_IN
michael@0 814 COMPOSITE_OPERATOR_OUT, // SVG_FECOMPOSITE_OPERATOR_OUT
michael@0 815 COMPOSITE_OPERATOR_ATOP, // SVG_FECOMPOSITE_OPERATOR_ATOP
michael@0 816 COMPOSITE_OPERATOR_XOR // SVG_FECOMPOSITE_OPERATOR_XOR
michael@0 817 };
michael@0 818 filter->SetAttribute(ATT_COMPOSITE_OPERATOR, (uint32_t)operators[op]);
michael@0 819 filter->SetInput(IN_COMPOSITE_IN_START, aSources[1]);
michael@0 820 filter->SetInput(IN_COMPOSITE_IN_START + 1, aSources[0]);
michael@0 821 }
michael@0 822 return filter;
michael@0 823 }
michael@0 824
michael@0 825 case PrimitiveType::Merge:
michael@0 826 {
michael@0 827 if (aSources.Length() == 0) {
michael@0 828 return nullptr;
michael@0 829 }
michael@0 830 if (aSources.Length() == 1) {
michael@0 831 return aSources[0];
michael@0 832 }
michael@0 833 RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::COMPOSITE);
michael@0 834 filter->SetAttribute(ATT_COMPOSITE_OPERATOR, (uint32_t)COMPOSITE_OPERATOR_OVER);
michael@0 835 for (size_t i = 0; i < aSources.Length(); i++) {
michael@0 836 filter->SetInput(IN_COMPOSITE_IN_START + i, aSources[i]);
michael@0 837 }
michael@0 838 return filter;
michael@0 839 }
michael@0 840
michael@0 841 case PrimitiveType::GaussianBlur:
michael@0 842 {
michael@0 843 return FilterWrappers::GaussianBlur(aDT, aSources[0],
michael@0 844 atts.GetSize(eGaussianBlurStdDeviation));
michael@0 845 }
michael@0 846
michael@0 847 case PrimitiveType::DropShadow:
michael@0 848 {
michael@0 849 RefPtr<FilterNode> alpha = FilterWrappers::ToAlpha(aDT, aSources[0]);
michael@0 850 RefPtr<FilterNode> blur = FilterWrappers::GaussianBlur(aDT, alpha,
michael@0 851 atts.GetSize(eDropShadowStdDeviation));
michael@0 852 RefPtr<FilterNode> offsetBlur = FilterWrappers::Offset(aDT, blur,
michael@0 853 atts.GetIntPoint(eDropShadowOffset));
michael@0 854 RefPtr<FilterNode> flood = aDT->CreateFilter(FilterType::FLOOD);
michael@0 855 Color color = atts.GetColor(eDropShadowColor);
michael@0 856 if (aDescription.InputColorSpace(0) == ColorSpace::LinearRGB) {
michael@0 857 color = Color(gsRGBToLinearRGBMap[uint8_t(color.r * 255)],
michael@0 858 gsRGBToLinearRGBMap[uint8_t(color.g * 255)],
michael@0 859 gsRGBToLinearRGBMap[uint8_t(color.b * 255)],
michael@0 860 color.a);
michael@0 861 }
michael@0 862 flood->SetAttribute(ATT_FLOOD_COLOR, color);
michael@0 863
michael@0 864 RefPtr<FilterNode> composite = aDT->CreateFilter(FilterType::COMPOSITE);
michael@0 865 composite->SetAttribute(ATT_COMPOSITE_OPERATOR, (uint32_t)COMPOSITE_OPERATOR_IN);
michael@0 866 composite->SetInput(IN_COMPOSITE_IN_START, offsetBlur);
michael@0 867 composite->SetInput(IN_COMPOSITE_IN_START + 1, flood);
michael@0 868
michael@0 869 RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::COMPOSITE);
michael@0 870 filter->SetAttribute(ATT_COMPOSITE_OPERATOR, (uint32_t)COMPOSITE_OPERATOR_OVER);
michael@0 871 filter->SetInput(IN_COMPOSITE_IN_START, composite);
michael@0 872 filter->SetInput(IN_COMPOSITE_IN_START + 1, aSources[0]);
michael@0 873 return filter;
michael@0 874 }
michael@0 875
michael@0 876 case PrimitiveType::DiffuseLighting:
michael@0 877 case PrimitiveType::SpecularLighting:
michael@0 878 {
michael@0 879 bool isSpecular =
michael@0 880 aDescription.Type() == PrimitiveType::SpecularLighting;
michael@0 881
michael@0 882 AttributeMap lightAttributes = atts.GetAttributeMap(eLightingLight);
michael@0 883
michael@0 884 if (lightAttributes.GetUint(eLightType) == eLightTypeNone) {
michael@0 885 return nullptr;
michael@0 886 }
michael@0 887
michael@0 888 enum { POINT = 0, SPOT, DISTANT } lightType = POINT;
michael@0 889
michael@0 890 switch (lightAttributes.GetUint(eLightType)) {
michael@0 891 case eLightTypePoint: lightType = POINT; break;
michael@0 892 case eLightTypeSpot: lightType = SPOT; break;
michael@0 893 case eLightTypeDistant: lightType = DISTANT; break;
michael@0 894 }
michael@0 895
michael@0 896 static const FilterType filterType[2][DISTANT+1] = {
michael@0 897 { FilterType::POINT_DIFFUSE, FilterType::SPOT_DIFFUSE, FilterType::DISTANT_DIFFUSE },
michael@0 898 { FilterType::POINT_SPECULAR, FilterType::SPOT_SPECULAR, FilterType::DISTANT_SPECULAR }
michael@0 899 };
michael@0 900 RefPtr<FilterNode> filter =
michael@0 901 aDT->CreateFilter(filterType[isSpecular][lightType]);
michael@0 902
michael@0 903 filter->SetAttribute(ATT_LIGHTING_COLOR,
michael@0 904 atts.GetColor(eLightingColor));
michael@0 905 filter->SetAttribute(ATT_LIGHTING_SURFACE_SCALE,
michael@0 906 atts.GetFloat(eLightingSurfaceScale));
michael@0 907 filter->SetAttribute(ATT_LIGHTING_KERNEL_UNIT_LENGTH,
michael@0 908 atts.GetSize(eLightingKernelUnitLength));
michael@0 909
michael@0 910 if (isSpecular) {
michael@0 911 filter->SetAttribute(ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT,
michael@0 912 atts.GetFloat(eSpecularLightingSpecularConstant));
michael@0 913 filter->SetAttribute(ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT,
michael@0 914 atts.GetFloat(eSpecularLightingSpecularExponent));
michael@0 915 } else {
michael@0 916 filter->SetAttribute(ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT,
michael@0 917 atts.GetFloat(eDiffuseLightingDiffuseConstant));
michael@0 918 }
michael@0 919
michael@0 920 switch (lightType) {
michael@0 921 case POINT:
michael@0 922 filter->SetAttribute(ATT_POINT_LIGHT_POSITION,
michael@0 923 lightAttributes.GetPoint3D(ePointLightPosition));
michael@0 924 break;
michael@0 925 case SPOT:
michael@0 926 filter->SetAttribute(ATT_SPOT_LIGHT_POSITION,
michael@0 927 lightAttributes.GetPoint3D(eSpotLightPosition));
michael@0 928 filter->SetAttribute(ATT_SPOT_LIGHT_POINTS_AT,
michael@0 929 lightAttributes.GetPoint3D(eSpotLightPointsAt));
michael@0 930 filter->SetAttribute(ATT_SPOT_LIGHT_FOCUS,
michael@0 931 lightAttributes.GetFloat(eSpotLightFocus));
michael@0 932 filter->SetAttribute(ATT_SPOT_LIGHT_LIMITING_CONE_ANGLE,
michael@0 933 lightAttributes.GetFloat(eSpotLightLimitingConeAngle));
michael@0 934 break;
michael@0 935 case DISTANT:
michael@0 936 filter->SetAttribute(ATT_DISTANT_LIGHT_AZIMUTH,
michael@0 937 lightAttributes.GetFloat(eDistantLightAzimuth));
michael@0 938 filter->SetAttribute(ATT_DISTANT_LIGHT_ELEVATION,
michael@0 939 lightAttributes.GetFloat(eDistantLightElevation));
michael@0 940 break;
michael@0 941 }
michael@0 942
michael@0 943 filter->SetInput(IN_LIGHTING_IN, aSources[0]);
michael@0 944
michael@0 945 return filter;
michael@0 946 }
michael@0 947
michael@0 948 case PrimitiveType::Image:
michael@0 949 {
michael@0 950 Matrix TM = atts.GetMatrix(eImageTransform);
michael@0 951 if (!TM.Determinant()) {
michael@0 952 return nullptr;
michael@0 953 }
michael@0 954
michael@0 955 // Pull the image from the additional image list using the index that's
michael@0 956 // stored in the primitive description.
michael@0 957 RefPtr<SourceSurface> inputImage =
michael@0 958 aInputImages[atts.GetUint(eImageInputIndex)];
michael@0 959
michael@0 960 RefPtr<FilterNode> transform = aDT->CreateFilter(FilterType::TRANSFORM);
michael@0 961 transform->SetInput(IN_TRANSFORM_IN, inputImage);
michael@0 962 transform->SetAttribute(ATT_TRANSFORM_MATRIX, TM);
michael@0 963 transform->SetAttribute(ATT_TRANSFORM_FILTER, atts.GetUint(eImageFilter));
michael@0 964 return transform;
michael@0 965 }
michael@0 966
michael@0 967 default:
michael@0 968 return nullptr;
michael@0 969 }
michael@0 970 }
michael@0 971
michael@0 972 template<typename T>
michael@0 973 static const T&
michael@0 974 ElementForIndex(int32_t aIndex,
michael@0 975 const nsTArray<T>& aPrimitiveElements,
michael@0 976 const T& aSourceGraphicElement,
michael@0 977 const T& aFillPaintElement,
michael@0 978 const T& aStrokePaintElement)
michael@0 979 {
michael@0 980 switch (aIndex) {
michael@0 981 case FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic:
michael@0 982 case FilterPrimitiveDescription::kPrimitiveIndexSourceAlpha:
michael@0 983 return aSourceGraphicElement;
michael@0 984 case FilterPrimitiveDescription::kPrimitiveIndexFillPaint:
michael@0 985 return aFillPaintElement;
michael@0 986 case FilterPrimitiveDescription::kPrimitiveIndexStrokePaint:
michael@0 987 return aStrokePaintElement;
michael@0 988 default:
michael@0 989 MOZ_ASSERT(aIndex >= 0, "bad index");
michael@0 990 return aPrimitiveElements[aIndex];
michael@0 991 }
michael@0 992 }
michael@0 993
michael@0 994 static AlphaModel
michael@0 995 InputAlphaModelForPrimitive(const FilterPrimitiveDescription& aDescr,
michael@0 996 int32_t aInputIndex,
michael@0 997 AlphaModel aOriginalAlphaModel)
michael@0 998 {
michael@0 999 switch (aDescr.Type()) {
michael@0 1000 case PrimitiveType::Tile:
michael@0 1001 case PrimitiveType::Offset:
michael@0 1002 return aOriginalAlphaModel;
michael@0 1003
michael@0 1004 case PrimitiveType::ColorMatrix:
michael@0 1005 case PrimitiveType::ComponentTransfer:
michael@0 1006 return AlphaModel::Unpremultiplied;
michael@0 1007
michael@0 1008 case PrimitiveType::DisplacementMap:
michael@0 1009 return aInputIndex == 0 ?
michael@0 1010 AlphaModel::Premultiplied : AlphaModel::Unpremultiplied;
michael@0 1011
michael@0 1012 case PrimitiveType::ConvolveMatrix:
michael@0 1013 return aDescr.Attributes().GetBool(eConvolveMatrixPreserveAlpha) ?
michael@0 1014 AlphaModel::Unpremultiplied : AlphaModel::Premultiplied;
michael@0 1015
michael@0 1016 default:
michael@0 1017 return AlphaModel::Premultiplied;
michael@0 1018 }
michael@0 1019 }
michael@0 1020
michael@0 1021 static AlphaModel
michael@0 1022 OutputAlphaModelForPrimitive(const FilterPrimitiveDescription& aDescr,
michael@0 1023 const nsTArray<AlphaModel>& aInputAlphaModels)
michael@0 1024 {
michael@0 1025 if (aInputAlphaModels.Length()) {
michael@0 1026 // For filters with inputs, the output is premultiplied if and only if the
michael@0 1027 // first input is premultiplied.
michael@0 1028 return InputAlphaModelForPrimitive(aDescr, 0, aInputAlphaModels[0]);
michael@0 1029 }
michael@0 1030
michael@0 1031 // All filters without inputs produce premultiplied alpha.
michael@0 1032 return AlphaModel::Premultiplied;
michael@0 1033 }
michael@0 1034
michael@0 1035 // Returns the output FilterNode, in premultiplied sRGB space.
michael@0 1036 static TemporaryRef<FilterNode>
michael@0 1037 FilterNodeGraphFromDescription(DrawTarget* aDT,
michael@0 1038 const FilterDescription& aFilter,
michael@0 1039 const Rect& aResultNeededRect,
michael@0 1040 SourceSurface* aSourceGraphic,
michael@0 1041 const IntRect& aSourceGraphicRect,
michael@0 1042 SourceSurface* aFillPaint,
michael@0 1043 const IntRect& aFillPaintRect,
michael@0 1044 SourceSurface* aStrokePaint,
michael@0 1045 const IntRect& aStrokePaintRect,
michael@0 1046 nsTArray<RefPtr<SourceSurface>>& aAdditionalImages)
michael@0 1047 {
michael@0 1048 const nsTArray<FilterPrimitiveDescription>& primitives = aFilter.mPrimitives;
michael@0 1049 const IntRect& filterSpaceBounds = aFilter.mFilterSpaceBounds;
michael@0 1050
michael@0 1051 Rect resultNeededRect(aResultNeededRect);
michael@0 1052 resultNeededRect.RoundOut();
michael@0 1053
michael@0 1054 RefPtr<FilterCachedColorModels> sourceFilters[4];
michael@0 1055 nsTArray<RefPtr<FilterCachedColorModels> > primitiveFilters;
michael@0 1056
michael@0 1057 for (size_t i = 0; i < primitives.Length(); ++i) {
michael@0 1058 const FilterPrimitiveDescription& descr = primitives[i];
michael@0 1059
michael@0 1060 nsTArray<RefPtr<FilterNode> > inputFilterNodes;
michael@0 1061 nsTArray<IntRect> inputSourceRects;
michael@0 1062 nsTArray<AlphaModel> inputAlphaModels;
michael@0 1063
michael@0 1064 for (size_t j = 0; j < descr.NumberOfInputs(); j++) {
michael@0 1065
michael@0 1066 int32_t inputIndex = descr.InputPrimitiveIndex(j);
michael@0 1067 if (inputIndex < 0) {
michael@0 1068 inputSourceRects.AppendElement(filterSpaceBounds);
michael@0 1069 } else {
michael@0 1070 inputSourceRects.AppendElement(filterSpaceBounds.Intersect(
michael@0 1071 primitives[inputIndex].PrimitiveSubregion()));
michael@0 1072 }
michael@0 1073
michael@0 1074 RefPtr<FilterCachedColorModels> inputFilter;
michael@0 1075 if (inputIndex >= 0) {
michael@0 1076 MOZ_ASSERT(inputIndex < (int64_t)primitiveFilters.Length(), "out-of-bounds input index!");
michael@0 1077 inputFilter = primitiveFilters[inputIndex];
michael@0 1078 MOZ_ASSERT(inputFilter, "Referred to input filter that comes after the current one?");
michael@0 1079 } else {
michael@0 1080 int32_t sourceIndex = -inputIndex - 1;
michael@0 1081 MOZ_ASSERT(sourceIndex >= 0, "invalid source index");
michael@0 1082 MOZ_ASSERT(sourceIndex < 4, "invalid source index");
michael@0 1083 inputFilter = sourceFilters[sourceIndex];
michael@0 1084 if (!inputFilter) {
michael@0 1085 RefPtr<FilterNode> sourceFilterNode;
michael@0 1086
michael@0 1087 nsTArray<SourceSurface*> primitiveSurfaces;
michael@0 1088 nsTArray<IntRect> primitiveSurfaceRects;
michael@0 1089 RefPtr<SourceSurface> surf =
michael@0 1090 ElementForIndex(inputIndex, primitiveSurfaces,
michael@0 1091 aSourceGraphic, aFillPaint, aStrokePaint);
michael@0 1092 IntRect surfaceRect =
michael@0 1093 ElementForIndex(inputIndex, primitiveSurfaceRects,
michael@0 1094 aSourceGraphicRect, aFillPaintRect, aStrokePaintRect);
michael@0 1095 if (surf) {
michael@0 1096 IntPoint offset = surfaceRect.TopLeft();
michael@0 1097 sourceFilterNode = FilterWrappers::ForSurface(aDT, surf, offset);
michael@0 1098
michael@0 1099 if (inputIndex == FilterPrimitiveDescription::kPrimitiveIndexSourceAlpha) {
michael@0 1100 sourceFilterNode = FilterWrappers::ToAlpha(aDT, sourceFilterNode);
michael@0 1101 }
michael@0 1102 }
michael@0 1103
michael@0 1104 inputFilter = new FilterCachedColorModels(aDT, sourceFilterNode,
michael@0 1105 ColorModel::PremulSRGB());
michael@0 1106 sourceFilters[sourceIndex] = inputFilter;
michael@0 1107 }
michael@0 1108 }
michael@0 1109 MOZ_ASSERT(inputFilter);
michael@0 1110
michael@0 1111 AlphaModel inputAlphaModel =
michael@0 1112 InputAlphaModelForPrimitive(descr, j, inputFilter->OriginalAlphaModel());
michael@0 1113 inputAlphaModels.AppendElement(inputAlphaModel);
michael@0 1114 ColorModel inputColorModel(descr.InputColorSpace(j), inputAlphaModel);
michael@0 1115 inputFilterNodes.AppendElement(inputFilter->ForColorModel(inputColorModel));
michael@0 1116 }
michael@0 1117
michael@0 1118 RefPtr<FilterNode> primitiveFilterNode =
michael@0 1119 FilterNodeFromPrimitiveDescription(descr, aDT, inputFilterNodes,
michael@0 1120 inputSourceRects, aAdditionalImages);
michael@0 1121
michael@0 1122 if (primitiveFilterNode) {
michael@0 1123 IntRect cropRect = filterSpaceBounds.Intersect(descr.PrimitiveSubregion());
michael@0 1124 primitiveFilterNode = FilterWrappers::Crop(aDT, primitiveFilterNode, cropRect);
michael@0 1125 }
michael@0 1126
michael@0 1127 ColorModel outputColorModel(descr.OutputColorSpace(),
michael@0 1128 OutputAlphaModelForPrimitive(descr, inputAlphaModels));
michael@0 1129 RefPtr<FilterCachedColorModels> primitiveFilter =
michael@0 1130 new FilterCachedColorModels(aDT, primitiveFilterNode, outputColorModel);
michael@0 1131
michael@0 1132 primitiveFilters.AppendElement(primitiveFilter);
michael@0 1133 }
michael@0 1134
michael@0 1135 return primitiveFilters.LastElement()->ForColorModel(ColorModel::PremulSRGB());
michael@0 1136 }
michael@0 1137
michael@0 1138 // FilterSupport
michael@0 1139
michael@0 1140 void
michael@0 1141 FilterSupport::RenderFilterDescription(DrawTarget* aDT,
michael@0 1142 const FilterDescription& aFilter,
michael@0 1143 const Rect& aRenderRect,
michael@0 1144 SourceSurface* aSourceGraphic,
michael@0 1145 const IntRect& aSourceGraphicRect,
michael@0 1146 SourceSurface* aFillPaint,
michael@0 1147 const IntRect& aFillPaintRect,
michael@0 1148 SourceSurface* aStrokePaint,
michael@0 1149 const IntRect& aStrokePaintRect,
michael@0 1150 nsTArray<RefPtr<SourceSurface>>& aAdditionalImages)
michael@0 1151 {
michael@0 1152 RefPtr<FilterNode> resultFilter =
michael@0 1153 FilterNodeGraphFromDescription(aDT, aFilter, aRenderRect,
michael@0 1154 aSourceGraphic, aSourceGraphicRect, aFillPaint, aFillPaintRect,
michael@0 1155 aStrokePaint, aStrokePaintRect, aAdditionalImages);
michael@0 1156
michael@0 1157 aDT->DrawFilter(resultFilter, aRenderRect, Point(0, 0));
michael@0 1158 }
michael@0 1159
michael@0 1160 static nsIntRegion
michael@0 1161 UnionOfRegions(const nsTArray<nsIntRegion>& aRegions)
michael@0 1162 {
michael@0 1163 nsIntRegion result;
michael@0 1164 for (size_t i = 0; i < aRegions.Length(); i++) {
michael@0 1165 result.Or(result, aRegions[i]);
michael@0 1166 }
michael@0 1167 return result;
michael@0 1168 }
michael@0 1169
michael@0 1170 static int32_t
michael@0 1171 InflateSizeForBlurStdDev(float aStdDev)
michael@0 1172 {
michael@0 1173 double size = std::min(aStdDev, kMaxStdDeviation) * (3 * sqrt(2 * M_PI) / 4) * 1.5;
michael@0 1174 return uint32_t(floor(size + 0.5));
michael@0 1175 }
michael@0 1176
michael@0 1177 static nsIntRegion
michael@0 1178 ResultChangeRegionForPrimitive(const FilterPrimitiveDescription& aDescription,
michael@0 1179 const nsTArray<nsIntRegion>& aInputChangeRegions)
michael@0 1180 {
michael@0 1181 const AttributeMap& atts = aDescription.Attributes();
michael@0 1182 switch (aDescription.Type()) {
michael@0 1183
michael@0 1184 case PrimitiveType::Empty:
michael@0 1185 case PrimitiveType::Flood:
michael@0 1186 case PrimitiveType::Turbulence:
michael@0 1187 case PrimitiveType::Image:
michael@0 1188 return nsIntRegion();
michael@0 1189
michael@0 1190 case PrimitiveType::Blend:
michael@0 1191 case PrimitiveType::Composite:
michael@0 1192 case PrimitiveType::Merge:
michael@0 1193 return UnionOfRegions(aInputChangeRegions);
michael@0 1194
michael@0 1195 case PrimitiveType::ColorMatrix:
michael@0 1196 case PrimitiveType::ComponentTransfer:
michael@0 1197 return aInputChangeRegions[0];
michael@0 1198
michael@0 1199 case PrimitiveType::Morphology:
michael@0 1200 {
michael@0 1201 Size radii = atts.GetSize(eMorphologyRadii);
michael@0 1202 int32_t rx = clamped(int32_t(ceil(radii.width)), 0, kMorphologyMaxRadius);
michael@0 1203 int32_t ry = clamped(int32_t(ceil(radii.height)), 0, kMorphologyMaxRadius);
michael@0 1204 return aInputChangeRegions[0].Inflated(nsIntMargin(ry, rx, ry, rx));
michael@0 1205 }
michael@0 1206
michael@0 1207 case PrimitiveType::Tile:
michael@0 1208 return ThebesIntRect(aDescription.PrimitiveSubregion());
michael@0 1209
michael@0 1210 case PrimitiveType::ConvolveMatrix:
michael@0 1211 {
michael@0 1212 Size kernelUnitLength = atts.GetSize(eConvolveMatrixKernelUnitLength);
michael@0 1213 IntSize kernelSize = atts.GetIntSize(eConvolveMatrixKernelSize);
michael@0 1214 IntPoint target = atts.GetIntPoint(eConvolveMatrixTarget);
michael@0 1215 nsIntMargin m(ceil(kernelUnitLength.width * (target.x)),
michael@0 1216 ceil(kernelUnitLength.height * (target.y)),
michael@0 1217 ceil(kernelUnitLength.width * (kernelSize.width - target.x - 1)),
michael@0 1218 ceil(kernelUnitLength.height * (kernelSize.height - target.y - 1)));
michael@0 1219 return aInputChangeRegions[0].Inflated(m);
michael@0 1220 }
michael@0 1221
michael@0 1222 case PrimitiveType::Offset:
michael@0 1223 {
michael@0 1224 IntPoint offset = atts.GetIntPoint(eOffsetOffset);
michael@0 1225 return aInputChangeRegions[0].MovedBy(offset.x, offset.y);
michael@0 1226 }
michael@0 1227
michael@0 1228 case PrimitiveType::DisplacementMap:
michael@0 1229 {
michael@0 1230 int32_t scale = ceil(abs(atts.GetFloat(eDisplacementMapScale)));
michael@0 1231 return aInputChangeRegions[0].Inflated(nsIntMargin(scale, scale, scale, scale));
michael@0 1232 }
michael@0 1233
michael@0 1234 case PrimitiveType::GaussianBlur:
michael@0 1235 {
michael@0 1236 Size stdDeviation = atts.GetSize(eGaussianBlurStdDeviation);
michael@0 1237 int32_t dx = InflateSizeForBlurStdDev(stdDeviation.width);
michael@0 1238 int32_t dy = InflateSizeForBlurStdDev(stdDeviation.height);
michael@0 1239 return aInputChangeRegions[0].Inflated(nsIntMargin(dy, dx, dy, dx));
michael@0 1240 }
michael@0 1241
michael@0 1242 case PrimitiveType::DropShadow:
michael@0 1243 {
michael@0 1244 IntPoint offset = atts.GetIntPoint(eDropShadowOffset);
michael@0 1245 nsIntRegion offsetRegion = aInputChangeRegions[0].MovedBy(offset.x, offset.y);
michael@0 1246 Size stdDeviation = atts.GetSize(eDropShadowStdDeviation);
michael@0 1247 int32_t dx = InflateSizeForBlurStdDev(stdDeviation.width);
michael@0 1248 int32_t dy = InflateSizeForBlurStdDev(stdDeviation.height);
michael@0 1249 nsIntRegion blurRegion = offsetRegion.Inflated(nsIntMargin(dy, dx, dy, dx));
michael@0 1250 blurRegion.Or(blurRegion, aInputChangeRegions[0]);
michael@0 1251 return blurRegion;
michael@0 1252 }
michael@0 1253
michael@0 1254 case PrimitiveType::DiffuseLighting:
michael@0 1255 case PrimitiveType::SpecularLighting:
michael@0 1256 {
michael@0 1257 Size kernelUnitLength = atts.GetSize(eLightingKernelUnitLength);
michael@0 1258 int32_t dx = ceil(kernelUnitLength.width);
michael@0 1259 int32_t dy = ceil(kernelUnitLength.height);
michael@0 1260 return aInputChangeRegions[0].Inflated(nsIntMargin(dy, dx, dy, dx));
michael@0 1261 }
michael@0 1262
michael@0 1263 default:
michael@0 1264 return nsIntRegion();
michael@0 1265 }
michael@0 1266 }
michael@0 1267
michael@0 1268 /* static */ nsIntRegion
michael@0 1269 FilterSupport::ComputeResultChangeRegion(const FilterDescription& aFilter,
michael@0 1270 const nsIntRegion& aSourceGraphicChange,
michael@0 1271 const nsIntRegion& aFillPaintChange,
michael@0 1272 const nsIntRegion& aStrokePaintChange)
michael@0 1273 {
michael@0 1274 const nsTArray<FilterPrimitiveDescription>& primitives = aFilter.mPrimitives;
michael@0 1275 nsTArray<nsIntRegion> resultChangeRegions;
michael@0 1276
michael@0 1277 for (int32_t i = 0; i < int32_t(primitives.Length()); ++i) {
michael@0 1278 const FilterPrimitiveDescription& descr = primitives[i];
michael@0 1279
michael@0 1280 nsTArray<nsIntRegion> inputChangeRegions;
michael@0 1281 for (size_t j = 0; j < descr.NumberOfInputs(); j++) {
michael@0 1282 int32_t inputIndex = descr.InputPrimitiveIndex(j);
michael@0 1283 MOZ_ASSERT(inputIndex < i, "bad input index");
michael@0 1284 nsIntRegion inputChangeRegion =
michael@0 1285 ElementForIndex(inputIndex, resultChangeRegions,
michael@0 1286 aSourceGraphicChange, aFillPaintChange,
michael@0 1287 aStrokePaintChange);
michael@0 1288 inputChangeRegions.AppendElement(inputChangeRegion);
michael@0 1289 }
michael@0 1290 nsIntRegion changeRegion =
michael@0 1291 ResultChangeRegionForPrimitive(descr, inputChangeRegions);
michael@0 1292 IntRect cropRect =
michael@0 1293 descr.PrimitiveSubregion().Intersect(aFilter.mFilterSpaceBounds);
michael@0 1294 changeRegion.And(changeRegion, ThebesIntRect(cropRect));
michael@0 1295 resultChangeRegions.AppendElement(changeRegion);
michael@0 1296 }
michael@0 1297
michael@0 1298 return resultChangeRegions[resultChangeRegions.Length() - 1];
michael@0 1299 }
michael@0 1300
michael@0 1301 static nsIntRegion
michael@0 1302 PostFilterExtentsForPrimitive(const FilterPrimitiveDescription& aDescription,
michael@0 1303 const nsTArray<nsIntRegion>& aInputExtents)
michael@0 1304 {
michael@0 1305 const AttributeMap& atts = aDescription.Attributes();
michael@0 1306 switch (aDescription.Type()) {
michael@0 1307
michael@0 1308 case PrimitiveType::Empty:
michael@0 1309 return nsIntRect();
michael@0 1310
michael@0 1311 case PrimitiveType::Composite:
michael@0 1312 {
michael@0 1313 uint32_t op = atts.GetUint(eCompositeOperator);
michael@0 1314 if (op == SVG_FECOMPOSITE_OPERATOR_ARITHMETIC) {
michael@0 1315 // The arithmetic composite primitive can draw outside the bounding
michael@0 1316 // box of its source images.
michael@0 1317 const nsTArray<float>& coefficients = atts.GetFloats(eCompositeCoefficients);
michael@0 1318 MOZ_ASSERT(coefficients.Length() == 4);
michael@0 1319
michael@0 1320 // The calculation is:
michael@0 1321 // r = c[0] * in[0] * in[1] + c[1] * in[0] + c[2] * in[1] + c[3]
michael@0 1322 nsIntRegion region;
michael@0 1323 if (coefficients[0] > 0.0f) {
michael@0 1324 region = aInputExtents[0].Intersect(aInputExtents[1]);
michael@0 1325 }
michael@0 1326 if (coefficients[1] > 0.0f) {
michael@0 1327 region.Or(region, aInputExtents[0]);
michael@0 1328 }
michael@0 1329 if (coefficients[2] > 0.0f) {
michael@0 1330 region.Or(region, aInputExtents[1]);
michael@0 1331 }
michael@0 1332 if (coefficients[3] > 0.0f) {
michael@0 1333 region = ThebesIntRect(aDescription.PrimitiveSubregion());
michael@0 1334 }
michael@0 1335 return region;
michael@0 1336 }
michael@0 1337 if (op == SVG_FECOMPOSITE_OPERATOR_IN) {
michael@0 1338 return aInputExtents[0].Intersect(aInputExtents[1]);
michael@0 1339 }
michael@0 1340 return ResultChangeRegionForPrimitive(aDescription, aInputExtents);
michael@0 1341 }
michael@0 1342
michael@0 1343 case PrimitiveType::Flood:
michael@0 1344 {
michael@0 1345 if (atts.GetColor(eFloodColor).a == 0.0f) {
michael@0 1346 return nsIntRect();
michael@0 1347 }
michael@0 1348 return ThebesIntRect(aDescription.PrimitiveSubregion());
michael@0 1349 }
michael@0 1350
michael@0 1351 case PrimitiveType::Turbulence:
michael@0 1352 case PrimitiveType::Image:
michael@0 1353 {
michael@0 1354 return ThebesIntRect(aDescription.PrimitiveSubregion());
michael@0 1355 }
michael@0 1356
michael@0 1357 case PrimitiveType::Morphology:
michael@0 1358 {
michael@0 1359 uint32_t op = atts.GetUint(eMorphologyOperator);
michael@0 1360 if (op == SVG_OPERATOR_ERODE) {
michael@0 1361 return aInputExtents[0];
michael@0 1362 }
michael@0 1363 Size radii = atts.GetSize(eMorphologyRadii);
michael@0 1364 int32_t rx = clamped(int32_t(ceil(radii.width)), 0, kMorphologyMaxRadius);
michael@0 1365 int32_t ry = clamped(int32_t(ceil(radii.height)), 0, kMorphologyMaxRadius);
michael@0 1366 return aInputExtents[0].Inflated(nsIntMargin(ry, rx, ry, rx));
michael@0 1367 }
michael@0 1368
michael@0 1369 default:
michael@0 1370 return ResultChangeRegionForPrimitive(aDescription, aInputExtents);
michael@0 1371 }
michael@0 1372 }
michael@0 1373
michael@0 1374 /* static */ nsIntRegion
michael@0 1375 FilterSupport::ComputePostFilterExtents(const FilterDescription& aFilter,
michael@0 1376 const nsIntRegion& aSourceGraphicExtents)
michael@0 1377 {
michael@0 1378 const nsTArray<FilterPrimitiveDescription>& primitives = aFilter.mPrimitives;
michael@0 1379 nsIntRegion filterSpace = ThebesIntRect(aFilter.mFilterSpaceBounds);
michael@0 1380 nsTArray<nsIntRegion> postFilterExtents;
michael@0 1381
michael@0 1382 for (int32_t i = 0; i < int32_t(primitives.Length()); ++i) {
michael@0 1383 const FilterPrimitiveDescription& descr = primitives[i];
michael@0 1384
michael@0 1385 nsTArray<nsIntRegion> inputExtents;
michael@0 1386 for (size_t j = 0; j < descr.NumberOfInputs(); j++) {
michael@0 1387 int32_t inputIndex = descr.InputPrimitiveIndex(j);
michael@0 1388 MOZ_ASSERT(inputIndex < i, "bad input index");
michael@0 1389 nsIntRegion inputExtent =
michael@0 1390 ElementForIndex(inputIndex, postFilterExtents,
michael@0 1391 aSourceGraphicExtents, filterSpace, filterSpace);
michael@0 1392 inputExtents.AppendElement(inputExtent);
michael@0 1393 }
michael@0 1394 nsIntRegion extent = PostFilterExtentsForPrimitive(descr, inputExtents);
michael@0 1395 IntRect cropRect =
michael@0 1396 descr.PrimitiveSubregion().Intersect(aFilter.mFilterSpaceBounds);
michael@0 1397 extent.And(extent, ThebesIntRect(cropRect));
michael@0 1398 postFilterExtents.AppendElement(extent);
michael@0 1399 }
michael@0 1400
michael@0 1401 return postFilterExtents[postFilterExtents.Length() - 1];
michael@0 1402 }
michael@0 1403
michael@0 1404 static nsIntRegion
michael@0 1405 SourceNeededRegionForPrimitive(const FilterPrimitiveDescription& aDescription,
michael@0 1406 const nsIntRegion& aResultNeededRegion,
michael@0 1407 int32_t aInputIndex)
michael@0 1408 {
michael@0 1409 const AttributeMap& atts = aDescription.Attributes();
michael@0 1410 switch (aDescription.Type()) {
michael@0 1411
michael@0 1412 case PrimitiveType::Flood:
michael@0 1413 case PrimitiveType::Turbulence:
michael@0 1414 case PrimitiveType::Image:
michael@0 1415 MOZ_CRASH("this shouldn't be called for filters without inputs");
michael@0 1416 return nsIntRegion();
michael@0 1417
michael@0 1418 case PrimitiveType::Empty:
michael@0 1419 return nsIntRegion();
michael@0 1420
michael@0 1421 case PrimitiveType::Blend:
michael@0 1422 case PrimitiveType::Composite:
michael@0 1423 case PrimitiveType::Merge:
michael@0 1424 case PrimitiveType::ColorMatrix:
michael@0 1425 case PrimitiveType::ComponentTransfer:
michael@0 1426 return aResultNeededRegion;
michael@0 1427
michael@0 1428 case PrimitiveType::Morphology:
michael@0 1429 {
michael@0 1430 Size radii = atts.GetSize(eMorphologyRadii);
michael@0 1431 int32_t rx = clamped(int32_t(ceil(radii.width)), 0, kMorphologyMaxRadius);
michael@0 1432 int32_t ry = clamped(int32_t(ceil(radii.height)), 0, kMorphologyMaxRadius);
michael@0 1433 return aResultNeededRegion.Inflated(nsIntMargin(ry, rx, ry, rx));
michael@0 1434 }
michael@0 1435
michael@0 1436 case PrimitiveType::Tile:
michael@0 1437 return nsIntRect(INT32_MIN/2, INT32_MIN/2, INT32_MAX, INT32_MAX);
michael@0 1438
michael@0 1439 case PrimitiveType::ConvolveMatrix:
michael@0 1440 {
michael@0 1441 Size kernelUnitLength = atts.GetSize(eConvolveMatrixKernelUnitLength);
michael@0 1442 IntSize kernelSize = atts.GetIntSize(eConvolveMatrixKernelSize);
michael@0 1443 IntPoint target = atts.GetIntPoint(eConvolveMatrixTarget);
michael@0 1444 nsIntMargin m(ceil(kernelUnitLength.width * (kernelSize.width - target.x - 1)),
michael@0 1445 ceil(kernelUnitLength.height * (kernelSize.height - target.y - 1)),
michael@0 1446 ceil(kernelUnitLength.width * (target.x)),
michael@0 1447 ceil(kernelUnitLength.height * (target.y)));
michael@0 1448 return aResultNeededRegion.Inflated(m);
michael@0 1449 }
michael@0 1450
michael@0 1451 case PrimitiveType::Offset:
michael@0 1452 {
michael@0 1453 IntPoint offset = atts.GetIntPoint(eOffsetOffset);
michael@0 1454 return aResultNeededRegion.MovedBy(-nsIntPoint(offset.x, offset.y));
michael@0 1455 }
michael@0 1456
michael@0 1457 case PrimitiveType::DisplacementMap:
michael@0 1458 {
michael@0 1459 if (aInputIndex == 1) {
michael@0 1460 return aResultNeededRegion;
michael@0 1461 }
michael@0 1462 int32_t scale = ceil(abs(atts.GetFloat(eDisplacementMapScale)));
michael@0 1463 return aResultNeededRegion.Inflated(nsIntMargin(scale, scale, scale, scale));
michael@0 1464 }
michael@0 1465
michael@0 1466 case PrimitiveType::GaussianBlur:
michael@0 1467 {
michael@0 1468 Size stdDeviation = atts.GetSize(eGaussianBlurStdDeviation);
michael@0 1469 int32_t dx = InflateSizeForBlurStdDev(stdDeviation.width);
michael@0 1470 int32_t dy = InflateSizeForBlurStdDev(stdDeviation.height);
michael@0 1471 return aResultNeededRegion.Inflated(nsIntMargin(dy, dx, dy, dx));
michael@0 1472 }
michael@0 1473
michael@0 1474 case PrimitiveType::DropShadow:
michael@0 1475 {
michael@0 1476 IntPoint offset = atts.GetIntPoint(eDropShadowOffset);
michael@0 1477 nsIntRegion offsetRegion =
michael@0 1478 aResultNeededRegion.MovedBy(-nsIntPoint(offset.x, offset.y));
michael@0 1479 Size stdDeviation = atts.GetSize(eDropShadowStdDeviation);
michael@0 1480 int32_t dx = InflateSizeForBlurStdDev(stdDeviation.width);
michael@0 1481 int32_t dy = InflateSizeForBlurStdDev(stdDeviation.height);
michael@0 1482 nsIntRegion blurRegion = offsetRegion.Inflated(nsIntMargin(dy, dx, dy, dx));
michael@0 1483 blurRegion.Or(blurRegion, aResultNeededRegion);
michael@0 1484 return blurRegion;
michael@0 1485 }
michael@0 1486
michael@0 1487 case PrimitiveType::DiffuseLighting:
michael@0 1488 case PrimitiveType::SpecularLighting:
michael@0 1489 {
michael@0 1490 Size kernelUnitLength = atts.GetSize(eLightingKernelUnitLength);
michael@0 1491 int32_t dx = ceil(kernelUnitLength.width);
michael@0 1492 int32_t dy = ceil(kernelUnitLength.height);
michael@0 1493 return aResultNeededRegion.Inflated(nsIntMargin(dy, dx, dy, dx));
michael@0 1494 }
michael@0 1495
michael@0 1496 default:
michael@0 1497 return nsIntRegion();
michael@0 1498 }
michael@0 1499
michael@0 1500 }
michael@0 1501
michael@0 1502 /* static */ void
michael@0 1503 FilterSupport::ComputeSourceNeededRegions(const FilterDescription& aFilter,
michael@0 1504 const nsIntRegion& aResultNeededRegion,
michael@0 1505 nsIntRegion& aSourceGraphicNeededRegion,
michael@0 1506 nsIntRegion& aFillPaintNeededRegion,
michael@0 1507 nsIntRegion& aStrokePaintNeededRegion)
michael@0 1508 {
michael@0 1509 const nsTArray<FilterPrimitiveDescription>& primitives = aFilter.mPrimitives;
michael@0 1510 nsTArray<nsIntRegion> primitiveNeededRegions;
michael@0 1511 primitiveNeededRegions.AppendElements(primitives.Length());
michael@0 1512
michael@0 1513 primitiveNeededRegions[primitives.Length() - 1] = aResultNeededRegion;
michael@0 1514
michael@0 1515 for (int32_t i = primitives.Length() - 1; i >= 0; --i) {
michael@0 1516 const FilterPrimitiveDescription& descr = primitives[i];
michael@0 1517 nsIntRegion neededRegion = primitiveNeededRegions[i];
michael@0 1518 neededRegion.And(neededRegion, ThebesIntRect(descr.PrimitiveSubregion()));
michael@0 1519
michael@0 1520 for (size_t j = 0; j < descr.NumberOfInputs(); j++) {
michael@0 1521 int32_t inputIndex = descr.InputPrimitiveIndex(j);
michael@0 1522 MOZ_ASSERT(inputIndex < i, "bad input index");
michael@0 1523 nsIntRegion* inputNeededRegion = const_cast<nsIntRegion*>(
michael@0 1524 &ElementForIndex(inputIndex, primitiveNeededRegions,
michael@0 1525 aSourceGraphicNeededRegion,
michael@0 1526 aFillPaintNeededRegion, aStrokePaintNeededRegion));
michael@0 1527 inputNeededRegion->Or(*inputNeededRegion,
michael@0 1528 SourceNeededRegionForPrimitive(descr, neededRegion, j));
michael@0 1529 }
michael@0 1530 }
michael@0 1531
michael@0 1532 aSourceGraphicNeededRegion.And(aSourceGraphicNeededRegion,
michael@0 1533 ThebesIntRect(aFilter.mFilterSpaceBounds));
michael@0 1534 }
michael@0 1535
michael@0 1536 // FilterPrimitiveDescription
michael@0 1537
michael@0 1538 FilterPrimitiveDescription::FilterPrimitiveDescription()
michael@0 1539 : mType(PrimitiveType::Empty)
michael@0 1540 , mOutputColorSpace(ColorSpace::SRGB)
michael@0 1541 , mIsTainted(false)
michael@0 1542 {
michael@0 1543 }
michael@0 1544
michael@0 1545 FilterPrimitiveDescription::FilterPrimitiveDescription(PrimitiveType aType)
michael@0 1546 : mType(aType)
michael@0 1547 , mOutputColorSpace(ColorSpace::SRGB)
michael@0 1548 , mIsTainted(false)
michael@0 1549 {
michael@0 1550 }
michael@0 1551
michael@0 1552 FilterPrimitiveDescription::FilterPrimitiveDescription(const FilterPrimitiveDescription& aOther)
michael@0 1553 : mType(aOther.mType)
michael@0 1554 , mAttributes(aOther.mAttributes)
michael@0 1555 , mInputPrimitives(aOther.mInputPrimitives)
michael@0 1556 , mFilterPrimitiveSubregion(aOther.mFilterPrimitiveSubregion)
michael@0 1557 , mInputColorSpaces(aOther.mInputColorSpaces)
michael@0 1558 , mOutputColorSpace(aOther.mOutputColorSpace)
michael@0 1559 , mIsTainted(aOther.mIsTainted)
michael@0 1560 {
michael@0 1561 }
michael@0 1562
michael@0 1563 FilterPrimitiveDescription&
michael@0 1564 FilterPrimitiveDescription::operator=(const FilterPrimitiveDescription& aOther)
michael@0 1565 {
michael@0 1566 if (this != &aOther) {
michael@0 1567 mType = aOther.mType;
michael@0 1568 mAttributes = aOther.mAttributes;
michael@0 1569 mInputPrimitives = aOther.mInputPrimitives;
michael@0 1570 mFilterPrimitiveSubregion = aOther.mFilterPrimitiveSubregion;
michael@0 1571 mInputColorSpaces = aOther.mInputColorSpaces;
michael@0 1572 mOutputColorSpace = aOther.mOutputColorSpace;
michael@0 1573 mIsTainted = aOther.mIsTainted;
michael@0 1574 }
michael@0 1575 return *this;
michael@0 1576 }
michael@0 1577
michael@0 1578 bool
michael@0 1579 FilterPrimitiveDescription::operator==(const FilterPrimitiveDescription& aOther) const
michael@0 1580 {
michael@0 1581 return mType == aOther.mType &&
michael@0 1582 mFilterPrimitiveSubregion.IsEqualInterior(aOther.mFilterPrimitiveSubregion) &&
michael@0 1583 mOutputColorSpace == aOther.mOutputColorSpace &&
michael@0 1584 mIsTainted == aOther.mIsTainted &&
michael@0 1585 mInputPrimitives == aOther.mInputPrimitives &&
michael@0 1586 mInputColorSpaces == aOther.mInputColorSpaces &&
michael@0 1587 mAttributes == aOther.mAttributes;
michael@0 1588 }
michael@0 1589
michael@0 1590 // FilterDescription
michael@0 1591
michael@0 1592 bool
michael@0 1593 FilterDescription::operator==(const FilterDescription& aOther) const
michael@0 1594 {
michael@0 1595 return mFilterSpaceBounds.IsEqualInterior(aOther.mFilterSpaceBounds) &&
michael@0 1596 mPrimitives == aOther.mPrimitives;
michael@0 1597 }
michael@0 1598
michael@0 1599 // AttributeMap
michael@0 1600
michael@0 1601 // A class that wraps different types for easy storage in a hashtable. Only
michael@0 1602 // used by AttributeMap.
michael@0 1603 struct FilterAttribute {
michael@0 1604 FilterAttribute(const FilterAttribute& aOther);
michael@0 1605 ~FilterAttribute();
michael@0 1606
michael@0 1607 bool operator==(const FilterAttribute& aOther) const;
michael@0 1608 bool operator!=(const FilterAttribute& aOther) const
michael@0 1609 {
michael@0 1610 return !(*this == aOther);
michael@0 1611 }
michael@0 1612
michael@0 1613 AttributeType Type() const { return mType; }
michael@0 1614
michael@0 1615 #define MAKE_CONSTRUCTOR_AND_ACCESSOR_BASIC(type, typeLabel) \
michael@0 1616 FilterAttribute(type aValue) \
michael@0 1617 : mType(AttributeType::e##typeLabel), m##typeLabel(aValue) \
michael@0 1618 {} \
michael@0 1619 type As##typeLabel() { \
michael@0 1620 MOZ_ASSERT(mType == AttributeType::e##typeLabel); \
michael@0 1621 return m##typeLabel; \
michael@0 1622 }
michael@0 1623
michael@0 1624 #define MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(className) \
michael@0 1625 FilterAttribute(const className& aValue) \
michael@0 1626 : mType(AttributeType::e##className), m##className(new className(aValue)) \
michael@0 1627 {} \
michael@0 1628 className As##className() { \
michael@0 1629 MOZ_ASSERT(mType == AttributeType::e##className); \
michael@0 1630 return *m##className; \
michael@0 1631 }
michael@0 1632
michael@0 1633 MAKE_CONSTRUCTOR_AND_ACCESSOR_BASIC(bool, Bool)
michael@0 1634 MAKE_CONSTRUCTOR_AND_ACCESSOR_BASIC(uint32_t, Uint)
michael@0 1635 MAKE_CONSTRUCTOR_AND_ACCESSOR_BASIC(float, Float)
michael@0 1636 MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(Size)
michael@0 1637 MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(IntSize)
michael@0 1638 MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(IntPoint)
michael@0 1639 MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(Matrix)
michael@0 1640 MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(Matrix5x4)
michael@0 1641 MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(Point3D)
michael@0 1642 MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(Color)
michael@0 1643 MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(AttributeMap)
michael@0 1644
michael@0 1645 #undef MAKE_CONSTRUCTOR_AND_ACCESSOR_BASIC
michael@0 1646 #undef MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS
michael@0 1647
michael@0 1648 FilterAttribute(const float* aValue, uint32_t aLength)
michael@0 1649 : mType(AttributeType::eFloats)
michael@0 1650 {
michael@0 1651 mFloats = new nsTArray<float>();
michael@0 1652 mFloats->AppendElements(aValue, aLength);
michael@0 1653 }
michael@0 1654
michael@0 1655 const nsTArray<float>& AsFloats() const {
michael@0 1656 MOZ_ASSERT(mType == AttributeType::eFloats);
michael@0 1657 return *mFloats;
michael@0 1658 }
michael@0 1659
michael@0 1660 private:
michael@0 1661 const AttributeType mType;
michael@0 1662
michael@0 1663 union {
michael@0 1664 bool mBool;
michael@0 1665 uint32_t mUint;
michael@0 1666 float mFloat;
michael@0 1667 Size* mSize;
michael@0 1668 IntSize* mIntSize;
michael@0 1669 IntPoint* mIntPoint;
michael@0 1670 Matrix* mMatrix;
michael@0 1671 Matrix5x4* mMatrix5x4;
michael@0 1672 Point3D* mPoint3D;
michael@0 1673 Color* mColor;
michael@0 1674 AttributeMap* mAttributeMap;
michael@0 1675 nsTArray<float>* mFloats;
michael@0 1676 };
michael@0 1677 };
michael@0 1678
michael@0 1679 FilterAttribute::FilterAttribute(const FilterAttribute& aOther)
michael@0 1680 : mType(aOther.mType)
michael@0 1681 {
michael@0 1682 switch (mType) {
michael@0 1683 case AttributeType::eBool:
michael@0 1684 mBool = aOther.mBool;
michael@0 1685 break;
michael@0 1686 case AttributeType::eUint:
michael@0 1687 mUint = aOther.mUint;
michael@0 1688 break;
michael@0 1689 case AttributeType::eFloat:
michael@0 1690 mFloat = aOther.mFloat;
michael@0 1691 break;
michael@0 1692
michael@0 1693 #define HANDLE_CLASS(className) \
michael@0 1694 case AttributeType::e##className: \
michael@0 1695 m##className = new className(*aOther.m##className); \
michael@0 1696 break;
michael@0 1697
michael@0 1698 HANDLE_CLASS(Size)
michael@0 1699 HANDLE_CLASS(IntSize)
michael@0 1700 HANDLE_CLASS(IntPoint)
michael@0 1701 HANDLE_CLASS(Matrix)
michael@0 1702 HANDLE_CLASS(Matrix5x4)
michael@0 1703 HANDLE_CLASS(Point3D)
michael@0 1704 HANDLE_CLASS(Color)
michael@0 1705 HANDLE_CLASS(AttributeMap)
michael@0 1706
michael@0 1707 #undef HANDLE_CLASS
michael@0 1708
michael@0 1709 case AttributeType::eFloats:
michael@0 1710 mFloats = new nsTArray<float>(*aOther.mFloats);
michael@0 1711 break;
michael@0 1712 case AttributeType::Max:
michael@0 1713 break;
michael@0 1714 }
michael@0 1715 }
michael@0 1716
michael@0 1717 FilterAttribute::~FilterAttribute() {
michael@0 1718 switch (mType) {
michael@0 1719 case AttributeType::Max:
michael@0 1720 case AttributeType::eBool:
michael@0 1721 case AttributeType::eUint:
michael@0 1722 case AttributeType::eFloat:
michael@0 1723 break;
michael@0 1724
michael@0 1725 #define HANDLE_CLASS(className) \
michael@0 1726 case AttributeType::e##className: \
michael@0 1727 delete m##className; \
michael@0 1728 break;
michael@0 1729
michael@0 1730 HANDLE_CLASS(Size)
michael@0 1731 HANDLE_CLASS(IntSize)
michael@0 1732 HANDLE_CLASS(IntPoint)
michael@0 1733 HANDLE_CLASS(Matrix)
michael@0 1734 HANDLE_CLASS(Matrix5x4)
michael@0 1735 HANDLE_CLASS(Point3D)
michael@0 1736 HANDLE_CLASS(Color)
michael@0 1737 HANDLE_CLASS(AttributeMap)
michael@0 1738
michael@0 1739 #undef HANDLE_CLASS
michael@0 1740
michael@0 1741 case AttributeType::eFloats:
michael@0 1742 delete mFloats;
michael@0 1743 break;
michael@0 1744 }
michael@0 1745 }
michael@0 1746
michael@0 1747 bool
michael@0 1748 FilterAttribute::operator==(const FilterAttribute& aOther) const
michael@0 1749 {
michael@0 1750 if (mType != aOther.mType) {
michael@0 1751 return false;
michael@0 1752 }
michael@0 1753
michael@0 1754 switch (mType) {
michael@0 1755
michael@0 1756 #define HANDLE_TYPE(typeName) \
michael@0 1757 case AttributeType::e##typeName: \
michael@0 1758 return m##typeName == aOther.m##typeName;
michael@0 1759
michael@0 1760 HANDLE_TYPE(Bool)
michael@0 1761 HANDLE_TYPE(Uint)
michael@0 1762 HANDLE_TYPE(Float)
michael@0 1763 HANDLE_TYPE(Size)
michael@0 1764 HANDLE_TYPE(IntSize)
michael@0 1765 HANDLE_TYPE(IntPoint)
michael@0 1766 HANDLE_TYPE(Matrix)
michael@0 1767 HANDLE_TYPE(Matrix5x4)
michael@0 1768 HANDLE_TYPE(Point3D)
michael@0 1769 HANDLE_TYPE(Color)
michael@0 1770 HANDLE_TYPE(AttributeMap)
michael@0 1771 HANDLE_TYPE(Floats)
michael@0 1772
michael@0 1773 #undef HANDLE_TYPE
michael@0 1774
michael@0 1775 default:
michael@0 1776 return false;
michael@0 1777 }
michael@0 1778 }
michael@0 1779
michael@0 1780 typedef FilterAttribute Attribute;
michael@0 1781
michael@0 1782 AttributeMap::AttributeMap()
michael@0 1783 {
michael@0 1784 }
michael@0 1785
michael@0 1786 AttributeMap::~AttributeMap()
michael@0 1787 {
michael@0 1788 }
michael@0 1789
michael@0 1790 static PLDHashOperator
michael@0 1791 CopyAttribute(const uint32_t& aAttributeName,
michael@0 1792 Attribute* aAttribute,
michael@0 1793 void* aAttributes)
michael@0 1794 {
michael@0 1795 typedef nsClassHashtable<nsUint32HashKey, Attribute> Map;
michael@0 1796 Map* map = static_cast<Map*>(aAttributes);
michael@0 1797 map->Put(aAttributeName, new Attribute(*aAttribute));
michael@0 1798 return PL_DHASH_NEXT;
michael@0 1799 }
michael@0 1800
michael@0 1801 AttributeMap::AttributeMap(const AttributeMap& aOther)
michael@0 1802 {
michael@0 1803 aOther.mMap.EnumerateRead(CopyAttribute, &mMap);
michael@0 1804 }
michael@0 1805
michael@0 1806 AttributeMap&
michael@0 1807 AttributeMap::operator=(const AttributeMap& aOther)
michael@0 1808 {
michael@0 1809 if (this != &aOther) {
michael@0 1810 mMap.Clear();
michael@0 1811 aOther.mMap.EnumerateRead(CopyAttribute, &mMap);
michael@0 1812 }
michael@0 1813 return *this;
michael@0 1814 }
michael@0 1815
michael@0 1816 namespace {
michael@0 1817 struct MatchingMap {
michael@0 1818 typedef nsClassHashtable<nsUint32HashKey, Attribute> Map;
michael@0 1819 const Map& map;
michael@0 1820 bool matches;
michael@0 1821 };
michael@0 1822 }
michael@0 1823
michael@0 1824 static PLDHashOperator
michael@0 1825 CheckAttributeEquality(const uint32_t& aAttributeName,
michael@0 1826 Attribute* aAttribute,
michael@0 1827 void* aMatchingMap)
michael@0 1828 {
michael@0 1829 MatchingMap& matchingMap = *static_cast<MatchingMap*>(aMatchingMap);
michael@0 1830 Attribute* matchingAttribute = matchingMap.map.Get(aAttributeName);
michael@0 1831 if (!matchingAttribute ||
michael@0 1832 *matchingAttribute != *aAttribute) {
michael@0 1833 matchingMap.matches = false;
michael@0 1834 return PL_DHASH_STOP;
michael@0 1835 }
michael@0 1836 return PL_DHASH_NEXT;
michael@0 1837 }
michael@0 1838
michael@0 1839 bool
michael@0 1840 AttributeMap::operator==(const AttributeMap& aOther) const
michael@0 1841 {
michael@0 1842 if (mMap.Count() != aOther.mMap.Count()) {
michael@0 1843 return false;
michael@0 1844 }
michael@0 1845
michael@0 1846 MatchingMap matchingMap = { mMap, true };
michael@0 1847 aOther.mMap.EnumerateRead(CheckAttributeEquality, &matchingMap);
michael@0 1848 return matchingMap.matches;
michael@0 1849 }
michael@0 1850
michael@0 1851 namespace {
michael@0 1852 struct HandlerWithUserData
michael@0 1853 {
michael@0 1854 AttributeMap::AttributeHandleCallback handler;
michael@0 1855 void* userData;
michael@0 1856 };
michael@0 1857 }
michael@0 1858
michael@0 1859 static PLDHashOperator
michael@0 1860 PassAttributeToHandleCallback(const uint32_t& aAttributeName,
michael@0 1861 Attribute* aAttribute,
michael@0 1862 void* aHandlerWithUserData)
michael@0 1863 {
michael@0 1864 HandlerWithUserData* handlerWithUserData =
michael@0 1865 static_cast<HandlerWithUserData*>(aHandlerWithUserData);
michael@0 1866 return handlerWithUserData->handler(AttributeName(aAttributeName),
michael@0 1867 aAttribute->Type(),
michael@0 1868 handlerWithUserData->userData) ?
michael@0 1869 PL_DHASH_NEXT : PL_DHASH_STOP;
michael@0 1870 }
michael@0 1871
michael@0 1872 void
michael@0 1873 AttributeMap::EnumerateRead(AttributeMap::AttributeHandleCallback aCallback, void* aUserData) const
michael@0 1874 {
michael@0 1875 HandlerWithUserData handlerWithUserData = { aCallback, aUserData };
michael@0 1876 mMap.EnumerateRead(PassAttributeToHandleCallback, &handlerWithUserData);
michael@0 1877 }
michael@0 1878
michael@0 1879 uint32_t
michael@0 1880 AttributeMap::Count() const
michael@0 1881 {
michael@0 1882 return mMap.Count();
michael@0 1883 }
michael@0 1884
michael@0 1885 #define MAKE_ATTRIBUTE_HANDLERS_BASIC(type, typeLabel, defaultValue) \
michael@0 1886 type \
michael@0 1887 AttributeMap::Get##typeLabel(AttributeName aName) const { \
michael@0 1888 Attribute* value = mMap.Get(aName); \
michael@0 1889 return value ? value->As##typeLabel() : defaultValue; \
michael@0 1890 } \
michael@0 1891 void \
michael@0 1892 AttributeMap::Set(AttributeName aName, type aValue) { \
michael@0 1893 mMap.Remove(aName); \
michael@0 1894 mMap.Put(aName, new Attribute(aValue)); \
michael@0 1895 }
michael@0 1896
michael@0 1897 #define MAKE_ATTRIBUTE_HANDLERS_CLASS(className) \
michael@0 1898 className \
michael@0 1899 AttributeMap::Get##className(AttributeName aName) const { \
michael@0 1900 Attribute* value = mMap.Get(aName); \
michael@0 1901 return value ? value->As##className() : className(); \
michael@0 1902 } \
michael@0 1903 void \
michael@0 1904 AttributeMap::Set(AttributeName aName, const className& aValue) { \
michael@0 1905 mMap.Remove(aName); \
michael@0 1906 mMap.Put(aName, new Attribute(aValue)); \
michael@0 1907 }
michael@0 1908
michael@0 1909 MAKE_ATTRIBUTE_HANDLERS_BASIC(bool, Bool, false)
michael@0 1910 MAKE_ATTRIBUTE_HANDLERS_BASIC(uint32_t, Uint, 0)
michael@0 1911 MAKE_ATTRIBUTE_HANDLERS_BASIC(float, Float, 0)
michael@0 1912 MAKE_ATTRIBUTE_HANDLERS_CLASS(Size)
michael@0 1913 MAKE_ATTRIBUTE_HANDLERS_CLASS(IntSize)
michael@0 1914 MAKE_ATTRIBUTE_HANDLERS_CLASS(IntPoint)
michael@0 1915 MAKE_ATTRIBUTE_HANDLERS_CLASS(Matrix)
michael@0 1916 MAKE_ATTRIBUTE_HANDLERS_CLASS(Matrix5x4)
michael@0 1917 MAKE_ATTRIBUTE_HANDLERS_CLASS(Point3D)
michael@0 1918 MAKE_ATTRIBUTE_HANDLERS_CLASS(Color)
michael@0 1919 MAKE_ATTRIBUTE_HANDLERS_CLASS(AttributeMap)
michael@0 1920
michael@0 1921 #undef MAKE_ATTRIBUTE_HANDLERS_BASIC
michael@0 1922 #undef MAKE_ATTRIBUTE_HANDLERS_CLASS
michael@0 1923
michael@0 1924 const nsTArray<float>&
michael@0 1925 AttributeMap::GetFloats(AttributeName aName) const
michael@0 1926 {
michael@0 1927 Attribute* value = mMap.Get(aName);
michael@0 1928 if (!value) {
michael@0 1929 value = new Attribute(nullptr, 0);
michael@0 1930 mMap.Put(aName, value);
michael@0 1931 }
michael@0 1932 return value->AsFloats();
michael@0 1933 }
michael@0 1934
michael@0 1935 void
michael@0 1936 AttributeMap::Set(AttributeName aName, const float* aValues, int32_t aLength)
michael@0 1937 {
michael@0 1938 mMap.Remove(aName);
michael@0 1939 mMap.Put(aName, new Attribute(aValues, aLength));
michael@0 1940 }
michael@0 1941
michael@0 1942 }
michael@0 1943 }

mercurial