Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
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 | } |