1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "FilterNodeD2D1.h"
8 
9 #include "Logging.h"
10 
11 #include "SourceSurfaceD2D1.h"
12 #include "DrawTargetD2D1.h"
13 #include "ExtendInputEffectD2D1.h"
14 
15 namespace mozilla {
16 namespace gfx {
17 
D2DAlphaMode(uint32_t aMode)18 D2D1_COLORMATRIX_ALPHA_MODE D2DAlphaMode(uint32_t aMode) {
19   switch (aMode) {
20     case ALPHA_MODE_PREMULTIPLIED:
21       return D2D1_COLORMATRIX_ALPHA_MODE_PREMULTIPLIED;
22     case ALPHA_MODE_STRAIGHT:
23       return D2D1_COLORMATRIX_ALPHA_MODE_STRAIGHT;
24     default:
25       MOZ_CRASH("GFX: Unknown enum value D2DAlphaMode!");
26   }
27 
28   return D2D1_COLORMATRIX_ALPHA_MODE_PREMULTIPLIED;
29 }
30 
D2DAffineTransformInterpolationMode(SamplingFilter aSamplingFilter)31 D2D1_2DAFFINETRANSFORM_INTERPOLATION_MODE D2DAffineTransformInterpolationMode(
32     SamplingFilter aSamplingFilter) {
33   switch (aSamplingFilter) {
34     case SamplingFilter::GOOD:
35       return D2D1_2DAFFINETRANSFORM_INTERPOLATION_MODE_LINEAR;
36     case SamplingFilter::LINEAR:
37       return D2D1_2DAFFINETRANSFORM_INTERPOLATION_MODE_LINEAR;
38     case SamplingFilter::POINT:
39       return D2D1_2DAFFINETRANSFORM_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
40     default:
41       MOZ_CRASH("GFX: Unknown enum value D2DAffineTIM!");
42   }
43 
44   return D2D1_2DAFFINETRANSFORM_INTERPOLATION_MODE_LINEAR;
45 }
46 
D2DBlendMode(uint32_t aMode)47 D2D1_BLEND_MODE D2DBlendMode(uint32_t aMode) {
48   switch (aMode) {
49     case BLEND_MODE_DARKEN:
50       return D2D1_BLEND_MODE_DARKEN;
51     case BLEND_MODE_LIGHTEN:
52       return D2D1_BLEND_MODE_LIGHTEN;
53     case BLEND_MODE_MULTIPLY:
54       return D2D1_BLEND_MODE_MULTIPLY;
55     case BLEND_MODE_SCREEN:
56       return D2D1_BLEND_MODE_SCREEN;
57     case BLEND_MODE_OVERLAY:
58       return D2D1_BLEND_MODE_OVERLAY;
59     case BLEND_MODE_COLOR_DODGE:
60       return D2D1_BLEND_MODE_COLOR_DODGE;
61     case BLEND_MODE_COLOR_BURN:
62       return D2D1_BLEND_MODE_COLOR_BURN;
63     case BLEND_MODE_HARD_LIGHT:
64       return D2D1_BLEND_MODE_HARD_LIGHT;
65     case BLEND_MODE_SOFT_LIGHT:
66       return D2D1_BLEND_MODE_SOFT_LIGHT;
67     case BLEND_MODE_DIFFERENCE:
68       return D2D1_BLEND_MODE_DIFFERENCE;
69     case BLEND_MODE_EXCLUSION:
70       return D2D1_BLEND_MODE_EXCLUSION;
71     case BLEND_MODE_HUE:
72       return D2D1_BLEND_MODE_HUE;
73     case BLEND_MODE_SATURATION:
74       return D2D1_BLEND_MODE_SATURATION;
75     case BLEND_MODE_COLOR:
76       return D2D1_BLEND_MODE_COLOR;
77     case BLEND_MODE_LUMINOSITY:
78       return D2D1_BLEND_MODE_LUMINOSITY;
79 
80     default:
81       MOZ_CRASH("GFX: Unknown enum value D2DBlendMode!");
82   }
83 
84   return D2D1_BLEND_MODE_DARKEN;
85 }
86 
D2DMorphologyMode(uint32_t aMode)87 D2D1_MORPHOLOGY_MODE D2DMorphologyMode(uint32_t aMode) {
88   switch (aMode) {
89     case MORPHOLOGY_OPERATOR_DILATE:
90       return D2D1_MORPHOLOGY_MODE_DILATE;
91     case MORPHOLOGY_OPERATOR_ERODE:
92       return D2D1_MORPHOLOGY_MODE_ERODE;
93   }
94 
95   MOZ_CRASH("GFX: Unknown enum value D2DMorphologyMode!");
96   return D2D1_MORPHOLOGY_MODE_DILATE;
97 }
98 
D2DTurbulenceNoise(uint32_t aMode)99 D2D1_TURBULENCE_NOISE D2DTurbulenceNoise(uint32_t aMode) {
100   switch (aMode) {
101     case TURBULENCE_TYPE_FRACTAL_NOISE:
102       return D2D1_TURBULENCE_NOISE_FRACTAL_SUM;
103     case TURBULENCE_TYPE_TURBULENCE:
104       return D2D1_TURBULENCE_NOISE_TURBULENCE;
105   }
106 
107   MOZ_CRASH("GFX: Unknown enum value D2DTurbulenceNoise!");
108   return D2D1_TURBULENCE_NOISE_TURBULENCE;
109 }
110 
D2DFilterCompositionMode(uint32_t aMode)111 D2D1_COMPOSITE_MODE D2DFilterCompositionMode(uint32_t aMode) {
112   switch (aMode) {
113     case COMPOSITE_OPERATOR_OVER:
114       return D2D1_COMPOSITE_MODE_SOURCE_OVER;
115     case COMPOSITE_OPERATOR_IN:
116       return D2D1_COMPOSITE_MODE_SOURCE_IN;
117     case COMPOSITE_OPERATOR_OUT:
118       return D2D1_COMPOSITE_MODE_SOURCE_OUT;
119     case COMPOSITE_OPERATOR_ATOP:
120       return D2D1_COMPOSITE_MODE_SOURCE_ATOP;
121     case COMPOSITE_OPERATOR_XOR:
122       return D2D1_COMPOSITE_MODE_XOR;
123   }
124 
125   MOZ_CRASH("GFX: Unknown enum value D2DFilterCompositionMode!");
126   return D2D1_COMPOSITE_MODE_SOURCE_OVER;
127 }
128 
D2DChannelSelector(uint32_t aMode)129 D2D1_CHANNEL_SELECTOR D2DChannelSelector(uint32_t aMode) {
130   switch (aMode) {
131     case COLOR_CHANNEL_R:
132       return D2D1_CHANNEL_SELECTOR_R;
133     case COLOR_CHANNEL_G:
134       return D2D1_CHANNEL_SELECTOR_G;
135     case COLOR_CHANNEL_B:
136       return D2D1_CHANNEL_SELECTOR_B;
137     case COLOR_CHANNEL_A:
138       return D2D1_CHANNEL_SELECTOR_A;
139   }
140 
141   MOZ_CRASH("GFX: Unknown enum value D2DChannelSelector!");
142   return D2D1_CHANNEL_SELECTOR_R;
143 }
144 
GetImageForSourceSurface(DrawTarget * aDT,SourceSurface * aSurface)145 already_AddRefed<ID2D1Image> GetImageForSourceSurface(DrawTarget* aDT,
146                                                       SourceSurface* aSurface) {
147   if (aDT->IsTiledDrawTarget() || aDT->IsDualDrawTarget() ||
148       aDT->IsCaptureDT()) {
149     gfxDevCrash(LogReason::FilterNodeD2D1Target)
150         << "Incompatible draw target type! " << (int)aDT->IsTiledDrawTarget()
151         << " " << (int)aDT->IsDualDrawTarget();
152     return nullptr;
153   }
154   switch (aDT->GetBackendType()) {
155     case BackendType::DIRECT2D1_1:
156       return static_cast<DrawTargetD2D1*>(aDT)->GetImageForSurface(
157           aSurface, ExtendMode::CLAMP);
158     default:
159       gfxDevCrash(LogReason::FilterNodeD2D1Backend)
160           << "Unknown draw target type! " << (int)aDT->GetBackendType();
161       return nullptr;
162   }
163 }
164 
ConvertValue(FilterType aType,uint32_t aAttribute,uint32_t aValue)165 uint32_t ConvertValue(FilterType aType, uint32_t aAttribute, uint32_t aValue) {
166   switch (aType) {
167     case FilterType::COLOR_MATRIX:
168       if (aAttribute == ATT_COLOR_MATRIX_ALPHA_MODE) {
169         aValue = D2DAlphaMode(aValue);
170       }
171       break;
172     case FilterType::TRANSFORM:
173       if (aAttribute == ATT_TRANSFORM_FILTER) {
174         aValue = D2DAffineTransformInterpolationMode(SamplingFilter(aValue));
175       }
176       break;
177     case FilterType::BLEND:
178       if (aAttribute == ATT_BLEND_BLENDMODE) {
179         aValue = D2DBlendMode(aValue);
180       }
181       break;
182     case FilterType::MORPHOLOGY:
183       if (aAttribute == ATT_MORPHOLOGY_OPERATOR) {
184         aValue = D2DMorphologyMode(aValue);
185       }
186       break;
187     case FilterType::DISPLACEMENT_MAP:
188       if (aAttribute == ATT_DISPLACEMENT_MAP_X_CHANNEL ||
189           aAttribute == ATT_DISPLACEMENT_MAP_Y_CHANNEL) {
190         aValue = D2DChannelSelector(aValue);
191       }
192       break;
193     case FilterType::TURBULENCE:
194       if (aAttribute == ATT_TURBULENCE_TYPE) {
195         aValue = D2DTurbulenceNoise(aValue);
196       }
197       break;
198     case FilterType::COMPOSITE:
199       if (aAttribute == ATT_COMPOSITE_OPERATOR) {
200         aValue = D2DFilterCompositionMode(aValue);
201       }
202       break;
203     default:
204       break;
205   }
206 
207   return aValue;
208 }
209 
ConvertValue(FilterType aType,uint32_t aAttribute,IntSize & aValue)210 void ConvertValue(FilterType aType, uint32_t aAttribute, IntSize& aValue) {
211   switch (aType) {
212     case FilterType::MORPHOLOGY:
213       if (aAttribute == ATT_MORPHOLOGY_RADII) {
214         aValue.width *= 2;
215         aValue.width += 1;
216         aValue.height *= 2;
217         aValue.height += 1;
218       }
219       break;
220     default:
221       break;
222   }
223 }
224 
225 UINT32
GetD2D1InputForInput(FilterType aType,uint32_t aIndex)226 GetD2D1InputForInput(FilterType aType, uint32_t aIndex) { return aIndex; }
227 
228 #define CONVERT_PROP(moz2dname, d2dname) \
229   case ATT_##moz2dname:                  \
230     return D2D1_##d2dname
231 
232 UINT32
GetD2D1PropForAttribute(FilterType aType,uint32_t aIndex)233 GetD2D1PropForAttribute(FilterType aType, uint32_t aIndex) {
234   switch (aType) {
235     case FilterType::COLOR_MATRIX:
236       switch (aIndex) {
237         CONVERT_PROP(COLOR_MATRIX_MATRIX, COLORMATRIX_PROP_COLOR_MATRIX);
238         CONVERT_PROP(COLOR_MATRIX_ALPHA_MODE, COLORMATRIX_PROP_ALPHA_MODE);
239       }
240       break;
241     case FilterType::TRANSFORM:
242       switch (aIndex) {
243         CONVERT_PROP(TRANSFORM_MATRIX, 2DAFFINETRANSFORM_PROP_TRANSFORM_MATRIX);
244         CONVERT_PROP(TRANSFORM_FILTER,
245                      2DAFFINETRANSFORM_PROP_INTERPOLATION_MODE);
246       }
247     case FilterType::BLEND:
248       switch (aIndex) { CONVERT_PROP(BLEND_BLENDMODE, BLEND_PROP_MODE); }
249       break;
250     case FilterType::MORPHOLOGY:
251       switch (aIndex) {
252         CONVERT_PROP(MORPHOLOGY_OPERATOR, MORPHOLOGY_PROP_MODE);
253       }
254       break;
255     case FilterType::FLOOD:
256       switch (aIndex) { CONVERT_PROP(FLOOD_COLOR, FLOOD_PROP_COLOR); }
257       break;
258     case FilterType::TILE:
259       switch (aIndex) { CONVERT_PROP(TILE_SOURCE_RECT, TILE_PROP_RECT); }
260       break;
261     case FilterType::TABLE_TRANSFER:
262       switch (aIndex) {
263         CONVERT_PROP(TABLE_TRANSFER_DISABLE_R, TABLETRANSFER_PROP_RED_DISABLE);
264         CONVERT_PROP(TABLE_TRANSFER_DISABLE_G,
265                      TABLETRANSFER_PROP_GREEN_DISABLE);
266         CONVERT_PROP(TABLE_TRANSFER_DISABLE_B, TABLETRANSFER_PROP_BLUE_DISABLE);
267         CONVERT_PROP(TABLE_TRANSFER_DISABLE_A,
268                      TABLETRANSFER_PROP_ALPHA_DISABLE);
269         CONVERT_PROP(TABLE_TRANSFER_TABLE_R, TABLETRANSFER_PROP_RED_TABLE);
270         CONVERT_PROP(TABLE_TRANSFER_TABLE_G, TABLETRANSFER_PROP_GREEN_TABLE);
271         CONVERT_PROP(TABLE_TRANSFER_TABLE_B, TABLETRANSFER_PROP_BLUE_TABLE);
272         CONVERT_PROP(TABLE_TRANSFER_TABLE_A, TABLETRANSFER_PROP_ALPHA_TABLE);
273       }
274       break;
275     case FilterType::DISCRETE_TRANSFER:
276       switch (aIndex) {
277         CONVERT_PROP(DISCRETE_TRANSFER_DISABLE_R,
278                      DISCRETETRANSFER_PROP_RED_DISABLE);
279         CONVERT_PROP(DISCRETE_TRANSFER_DISABLE_G,
280                      DISCRETETRANSFER_PROP_GREEN_DISABLE);
281         CONVERT_PROP(DISCRETE_TRANSFER_DISABLE_B,
282                      DISCRETETRANSFER_PROP_BLUE_DISABLE);
283         CONVERT_PROP(DISCRETE_TRANSFER_DISABLE_A,
284                      DISCRETETRANSFER_PROP_ALPHA_DISABLE);
285         CONVERT_PROP(DISCRETE_TRANSFER_TABLE_R,
286                      DISCRETETRANSFER_PROP_RED_TABLE);
287         CONVERT_PROP(DISCRETE_TRANSFER_TABLE_G,
288                      DISCRETETRANSFER_PROP_GREEN_TABLE);
289         CONVERT_PROP(DISCRETE_TRANSFER_TABLE_B,
290                      DISCRETETRANSFER_PROP_BLUE_TABLE);
291         CONVERT_PROP(DISCRETE_TRANSFER_TABLE_A,
292                      DISCRETETRANSFER_PROP_ALPHA_TABLE);
293       }
294       break;
295     case FilterType::LINEAR_TRANSFER:
296       switch (aIndex) {
297         CONVERT_PROP(LINEAR_TRANSFER_DISABLE_R,
298                      LINEARTRANSFER_PROP_RED_DISABLE);
299         CONVERT_PROP(LINEAR_TRANSFER_DISABLE_G,
300                      LINEARTRANSFER_PROP_GREEN_DISABLE);
301         CONVERT_PROP(LINEAR_TRANSFER_DISABLE_B,
302                      LINEARTRANSFER_PROP_BLUE_DISABLE);
303         CONVERT_PROP(LINEAR_TRANSFER_DISABLE_A,
304                      LINEARTRANSFER_PROP_ALPHA_DISABLE);
305         CONVERT_PROP(LINEAR_TRANSFER_INTERCEPT_R,
306                      LINEARTRANSFER_PROP_RED_Y_INTERCEPT);
307         CONVERT_PROP(LINEAR_TRANSFER_INTERCEPT_G,
308                      LINEARTRANSFER_PROP_GREEN_Y_INTERCEPT);
309         CONVERT_PROP(LINEAR_TRANSFER_INTERCEPT_B,
310                      LINEARTRANSFER_PROP_BLUE_Y_INTERCEPT);
311         CONVERT_PROP(LINEAR_TRANSFER_INTERCEPT_A,
312                      LINEARTRANSFER_PROP_ALPHA_Y_INTERCEPT);
313         CONVERT_PROP(LINEAR_TRANSFER_SLOPE_R, LINEARTRANSFER_PROP_RED_SLOPE);
314         CONVERT_PROP(LINEAR_TRANSFER_SLOPE_G, LINEARTRANSFER_PROP_GREEN_SLOPE);
315         CONVERT_PROP(LINEAR_TRANSFER_SLOPE_B, LINEARTRANSFER_PROP_BLUE_SLOPE);
316         CONVERT_PROP(LINEAR_TRANSFER_SLOPE_A, LINEARTRANSFER_PROP_ALPHA_SLOPE);
317       }
318       break;
319     case FilterType::GAMMA_TRANSFER:
320       switch (aIndex) {
321         CONVERT_PROP(GAMMA_TRANSFER_DISABLE_R, GAMMATRANSFER_PROP_RED_DISABLE);
322         CONVERT_PROP(GAMMA_TRANSFER_DISABLE_G,
323                      GAMMATRANSFER_PROP_GREEN_DISABLE);
324         CONVERT_PROP(GAMMA_TRANSFER_DISABLE_B, GAMMATRANSFER_PROP_BLUE_DISABLE);
325         CONVERT_PROP(GAMMA_TRANSFER_DISABLE_A,
326                      GAMMATRANSFER_PROP_ALPHA_DISABLE);
327         CONVERT_PROP(GAMMA_TRANSFER_AMPLITUDE_R,
328                      GAMMATRANSFER_PROP_RED_AMPLITUDE);
329         CONVERT_PROP(GAMMA_TRANSFER_AMPLITUDE_G,
330                      GAMMATRANSFER_PROP_GREEN_AMPLITUDE);
331         CONVERT_PROP(GAMMA_TRANSFER_AMPLITUDE_B,
332                      GAMMATRANSFER_PROP_BLUE_AMPLITUDE);
333         CONVERT_PROP(GAMMA_TRANSFER_AMPLITUDE_A,
334                      GAMMATRANSFER_PROP_ALPHA_AMPLITUDE);
335         CONVERT_PROP(GAMMA_TRANSFER_EXPONENT_R,
336                      GAMMATRANSFER_PROP_RED_EXPONENT);
337         CONVERT_PROP(GAMMA_TRANSFER_EXPONENT_G,
338                      GAMMATRANSFER_PROP_GREEN_EXPONENT);
339         CONVERT_PROP(GAMMA_TRANSFER_EXPONENT_B,
340                      GAMMATRANSFER_PROP_BLUE_EXPONENT);
341         CONVERT_PROP(GAMMA_TRANSFER_EXPONENT_A,
342                      GAMMATRANSFER_PROP_ALPHA_EXPONENT);
343         CONVERT_PROP(GAMMA_TRANSFER_OFFSET_R, GAMMATRANSFER_PROP_RED_OFFSET);
344         CONVERT_PROP(GAMMA_TRANSFER_OFFSET_G, GAMMATRANSFER_PROP_GREEN_OFFSET);
345         CONVERT_PROP(GAMMA_TRANSFER_OFFSET_B, GAMMATRANSFER_PROP_BLUE_OFFSET);
346         CONVERT_PROP(GAMMA_TRANSFER_OFFSET_A, GAMMATRANSFER_PROP_ALPHA_OFFSET);
347       }
348       break;
349     case FilterType::CONVOLVE_MATRIX:
350       switch (aIndex) {
351         CONVERT_PROP(CONVOLVE_MATRIX_BIAS, CONVOLVEMATRIX_PROP_BIAS);
352         CONVERT_PROP(CONVOLVE_MATRIX_KERNEL_MATRIX,
353                      CONVOLVEMATRIX_PROP_KERNEL_MATRIX);
354         CONVERT_PROP(CONVOLVE_MATRIX_DIVISOR, CONVOLVEMATRIX_PROP_DIVISOR);
355         CONVERT_PROP(CONVOLVE_MATRIX_KERNEL_UNIT_LENGTH,
356                      CONVOLVEMATRIX_PROP_KERNEL_UNIT_LENGTH);
357         CONVERT_PROP(CONVOLVE_MATRIX_PRESERVE_ALPHA,
358                      CONVOLVEMATRIX_PROP_PRESERVE_ALPHA);
359       }
360     case FilterType::DISPLACEMENT_MAP:
361       switch (aIndex) {
362         CONVERT_PROP(DISPLACEMENT_MAP_SCALE, DISPLACEMENTMAP_PROP_SCALE);
363         CONVERT_PROP(DISPLACEMENT_MAP_X_CHANNEL,
364                      DISPLACEMENTMAP_PROP_X_CHANNEL_SELECT);
365         CONVERT_PROP(DISPLACEMENT_MAP_Y_CHANNEL,
366                      DISPLACEMENTMAP_PROP_Y_CHANNEL_SELECT);
367       }
368       break;
369     case FilterType::TURBULENCE:
370       switch (aIndex) {
371         CONVERT_PROP(TURBULENCE_BASE_FREQUENCY, TURBULENCE_PROP_BASE_FREQUENCY);
372         CONVERT_PROP(TURBULENCE_NUM_OCTAVES, TURBULENCE_PROP_NUM_OCTAVES);
373         CONVERT_PROP(TURBULENCE_SEED, TURBULENCE_PROP_SEED);
374         CONVERT_PROP(TURBULENCE_STITCHABLE, TURBULENCE_PROP_STITCHABLE);
375         CONVERT_PROP(TURBULENCE_TYPE, TURBULENCE_PROP_NOISE);
376       }
377       break;
378     case FilterType::ARITHMETIC_COMBINE:
379       switch (aIndex) {
380         CONVERT_PROP(ARITHMETIC_COMBINE_COEFFICIENTS,
381                      ARITHMETICCOMPOSITE_PROP_COEFFICIENTS);
382       }
383       break;
384     case FilterType::COMPOSITE:
385       switch (aIndex) { CONVERT_PROP(COMPOSITE_OPERATOR, COMPOSITE_PROP_MODE); }
386       break;
387     case FilterType::GAUSSIAN_BLUR:
388       switch (aIndex) {
389         CONVERT_PROP(GAUSSIAN_BLUR_STD_DEVIATION,
390                      GAUSSIANBLUR_PROP_STANDARD_DEVIATION);
391       }
392       break;
393     case FilterType::DIRECTIONAL_BLUR:
394       switch (aIndex) {
395         CONVERT_PROP(DIRECTIONAL_BLUR_STD_DEVIATION,
396                      DIRECTIONALBLUR_PROP_STANDARD_DEVIATION);
397         CONVERT_PROP(DIRECTIONAL_BLUR_DIRECTION, DIRECTIONALBLUR_PROP_ANGLE);
398       }
399       break;
400     case FilterType::POINT_DIFFUSE:
401       switch (aIndex) {
402         CONVERT_PROP(POINT_DIFFUSE_DIFFUSE_CONSTANT,
403                      POINTDIFFUSE_PROP_DIFFUSE_CONSTANT);
404         CONVERT_PROP(POINT_DIFFUSE_POSITION, POINTDIFFUSE_PROP_LIGHT_POSITION);
405         CONVERT_PROP(POINT_DIFFUSE_COLOR, POINTDIFFUSE_PROP_COLOR);
406         CONVERT_PROP(POINT_DIFFUSE_SURFACE_SCALE,
407                      POINTDIFFUSE_PROP_SURFACE_SCALE);
408         CONVERT_PROP(POINT_DIFFUSE_KERNEL_UNIT_LENGTH,
409                      POINTDIFFUSE_PROP_KERNEL_UNIT_LENGTH);
410       }
411       break;
412     case FilterType::SPOT_DIFFUSE:
413       switch (aIndex) {
414         CONVERT_PROP(SPOT_DIFFUSE_DIFFUSE_CONSTANT,
415                      SPOTDIFFUSE_PROP_DIFFUSE_CONSTANT);
416         CONVERT_PROP(SPOT_DIFFUSE_POINTS_AT, SPOTDIFFUSE_PROP_POINTS_AT);
417         CONVERT_PROP(SPOT_DIFFUSE_FOCUS, SPOTDIFFUSE_PROP_FOCUS);
418         CONVERT_PROP(SPOT_DIFFUSE_LIMITING_CONE_ANGLE,
419                      SPOTDIFFUSE_PROP_LIMITING_CONE_ANGLE);
420         CONVERT_PROP(SPOT_DIFFUSE_POSITION, SPOTDIFFUSE_PROP_LIGHT_POSITION);
421         CONVERT_PROP(SPOT_DIFFUSE_COLOR, SPOTDIFFUSE_PROP_COLOR);
422         CONVERT_PROP(SPOT_DIFFUSE_SURFACE_SCALE,
423                      SPOTDIFFUSE_PROP_SURFACE_SCALE);
424         CONVERT_PROP(SPOT_DIFFUSE_KERNEL_UNIT_LENGTH,
425                      SPOTDIFFUSE_PROP_KERNEL_UNIT_LENGTH);
426       }
427       break;
428     case FilterType::DISTANT_DIFFUSE:
429       switch (aIndex) {
430         CONVERT_PROP(DISTANT_DIFFUSE_DIFFUSE_CONSTANT,
431                      DISTANTDIFFUSE_PROP_DIFFUSE_CONSTANT);
432         CONVERT_PROP(DISTANT_DIFFUSE_AZIMUTH, DISTANTDIFFUSE_PROP_AZIMUTH);
433         CONVERT_PROP(DISTANT_DIFFUSE_ELEVATION, DISTANTDIFFUSE_PROP_ELEVATION);
434         CONVERT_PROP(DISTANT_DIFFUSE_COLOR, DISTANTDIFFUSE_PROP_COLOR);
435         CONVERT_PROP(DISTANT_DIFFUSE_SURFACE_SCALE,
436                      DISTANTDIFFUSE_PROP_SURFACE_SCALE);
437         CONVERT_PROP(DISTANT_DIFFUSE_KERNEL_UNIT_LENGTH,
438                      DISTANTDIFFUSE_PROP_KERNEL_UNIT_LENGTH);
439       }
440       break;
441     case FilterType::POINT_SPECULAR:
442       switch (aIndex) {
443         CONVERT_PROP(POINT_SPECULAR_SPECULAR_CONSTANT,
444                      POINTSPECULAR_PROP_SPECULAR_CONSTANT);
445         CONVERT_PROP(POINT_SPECULAR_SPECULAR_EXPONENT,
446                      POINTSPECULAR_PROP_SPECULAR_EXPONENT);
447         CONVERT_PROP(POINT_SPECULAR_POSITION,
448                      POINTSPECULAR_PROP_LIGHT_POSITION);
449         CONVERT_PROP(POINT_SPECULAR_COLOR, POINTSPECULAR_PROP_COLOR);
450         CONVERT_PROP(POINT_SPECULAR_SURFACE_SCALE,
451                      POINTSPECULAR_PROP_SURFACE_SCALE);
452         CONVERT_PROP(POINT_SPECULAR_KERNEL_UNIT_LENGTH,
453                      POINTSPECULAR_PROP_KERNEL_UNIT_LENGTH);
454       }
455       break;
456     case FilterType::SPOT_SPECULAR:
457       switch (aIndex) {
458         CONVERT_PROP(SPOT_SPECULAR_SPECULAR_CONSTANT,
459                      SPOTSPECULAR_PROP_SPECULAR_CONSTANT);
460         CONVERT_PROP(SPOT_SPECULAR_SPECULAR_EXPONENT,
461                      SPOTSPECULAR_PROP_SPECULAR_EXPONENT);
462         CONVERT_PROP(SPOT_SPECULAR_POINTS_AT, SPOTSPECULAR_PROP_POINTS_AT);
463         CONVERT_PROP(SPOT_SPECULAR_FOCUS, SPOTSPECULAR_PROP_FOCUS);
464         CONVERT_PROP(SPOT_SPECULAR_LIMITING_CONE_ANGLE,
465                      SPOTSPECULAR_PROP_LIMITING_CONE_ANGLE);
466         CONVERT_PROP(SPOT_SPECULAR_POSITION, SPOTSPECULAR_PROP_LIGHT_POSITION);
467         CONVERT_PROP(SPOT_SPECULAR_COLOR, SPOTSPECULAR_PROP_COLOR);
468         CONVERT_PROP(SPOT_SPECULAR_SURFACE_SCALE,
469                      SPOTSPECULAR_PROP_SURFACE_SCALE);
470         CONVERT_PROP(SPOT_SPECULAR_KERNEL_UNIT_LENGTH,
471                      SPOTSPECULAR_PROP_KERNEL_UNIT_LENGTH);
472       }
473       break;
474     case FilterType::DISTANT_SPECULAR:
475       switch (aIndex) {
476         CONVERT_PROP(DISTANT_SPECULAR_SPECULAR_CONSTANT,
477                      DISTANTSPECULAR_PROP_SPECULAR_CONSTANT);
478         CONVERT_PROP(DISTANT_SPECULAR_SPECULAR_EXPONENT,
479                      DISTANTSPECULAR_PROP_SPECULAR_EXPONENT);
480         CONVERT_PROP(DISTANT_SPECULAR_AZIMUTH, DISTANTSPECULAR_PROP_AZIMUTH);
481         CONVERT_PROP(DISTANT_SPECULAR_ELEVATION,
482                      DISTANTSPECULAR_PROP_ELEVATION);
483         CONVERT_PROP(DISTANT_SPECULAR_COLOR, DISTANTSPECULAR_PROP_COLOR);
484         CONVERT_PROP(DISTANT_SPECULAR_SURFACE_SCALE,
485                      DISTANTSPECULAR_PROP_SURFACE_SCALE);
486         CONVERT_PROP(DISTANT_SPECULAR_KERNEL_UNIT_LENGTH,
487                      DISTANTSPECULAR_PROP_KERNEL_UNIT_LENGTH);
488       }
489       break;
490     case FilterType::CROP:
491       switch (aIndex) { CONVERT_PROP(CROP_RECT, CROP_PROP_RECT); }
492       break;
493     default:
494       break;
495   }
496 
497   return UINT32_MAX;
498 }
499 
GetD2D1PropsForIntSize(FilterType aType,uint32_t aIndex,UINT32 * aPropWidth,UINT32 * aPropHeight)500 bool GetD2D1PropsForIntSize(FilterType aType, uint32_t aIndex,
501                             UINT32* aPropWidth, UINT32* aPropHeight) {
502   switch (aType) {
503     case FilterType::MORPHOLOGY:
504       if (aIndex == ATT_MORPHOLOGY_RADII) {
505         *aPropWidth = D2D1_MORPHOLOGY_PROP_WIDTH;
506         *aPropHeight = D2D1_MORPHOLOGY_PROP_HEIGHT;
507         return true;
508       }
509       break;
510     default:
511       break;
512   }
513   return false;
514 }
515 
GetCLDIDForFilterType(FilterType aType)516 static inline REFCLSID GetCLDIDForFilterType(FilterType aType) {
517   switch (aType) {
518     case FilterType::OPACITY:
519     case FilterType::COLOR_MATRIX:
520       return CLSID_D2D1ColorMatrix;
521     case FilterType::TRANSFORM:
522       return CLSID_D2D12DAffineTransform;
523     case FilterType::BLEND:
524       return CLSID_D2D1Blend;
525     case FilterType::MORPHOLOGY:
526       return CLSID_D2D1Morphology;
527     case FilterType::FLOOD:
528       return CLSID_D2D1Flood;
529     case FilterType::TILE:
530       return CLSID_D2D1Tile;
531     case FilterType::TABLE_TRANSFER:
532       return CLSID_D2D1TableTransfer;
533     case FilterType::LINEAR_TRANSFER:
534       return CLSID_D2D1LinearTransfer;
535     case FilterType::DISCRETE_TRANSFER:
536       return CLSID_D2D1DiscreteTransfer;
537     case FilterType::GAMMA_TRANSFER:
538       return CLSID_D2D1GammaTransfer;
539     case FilterType::DISPLACEMENT_MAP:
540       return CLSID_D2D1DisplacementMap;
541     case FilterType::TURBULENCE:
542       return CLSID_D2D1Turbulence;
543     case FilterType::ARITHMETIC_COMBINE:
544       return CLSID_D2D1ArithmeticComposite;
545     case FilterType::COMPOSITE:
546       return CLSID_D2D1Composite;
547     case FilterType::GAUSSIAN_BLUR:
548       return CLSID_D2D1GaussianBlur;
549     case FilterType::DIRECTIONAL_BLUR:
550       return CLSID_D2D1DirectionalBlur;
551     case FilterType::POINT_DIFFUSE:
552       return CLSID_D2D1PointDiffuse;
553     case FilterType::POINT_SPECULAR:
554       return CLSID_D2D1PointSpecular;
555     case FilterType::SPOT_DIFFUSE:
556       return CLSID_D2D1SpotDiffuse;
557     case FilterType::SPOT_SPECULAR:
558       return CLSID_D2D1SpotSpecular;
559     case FilterType::DISTANT_DIFFUSE:
560       return CLSID_D2D1DistantDiffuse;
561     case FilterType::DISTANT_SPECULAR:
562       return CLSID_D2D1DistantSpecular;
563     case FilterType::CROP:
564       return CLSID_D2D1Crop;
565     case FilterType::PREMULTIPLY:
566       return CLSID_D2D1Premultiply;
567     case FilterType::UNPREMULTIPLY:
568       return CLSID_D2D1UnPremultiply;
569     default:
570       break;
571   }
572   return GUID_NULL;
573 }
574 
IsTransferFilterType(FilterType aType)575 static bool IsTransferFilterType(FilterType aType) {
576   switch (aType) {
577     case FilterType::LINEAR_TRANSFER:
578     case FilterType::GAMMA_TRANSFER:
579     case FilterType::TABLE_TRANSFER:
580     case FilterType::DISCRETE_TRANSFER:
581       return true;
582     default:
583       return false;
584   }
585 }
586 
HasUnboundedOutputRegion(FilterType aType)587 static bool HasUnboundedOutputRegion(FilterType aType) {
588   if (IsTransferFilterType(aType)) {
589     return true;
590   }
591 
592   switch (aType) {
593     case FilterType::COLOR_MATRIX:
594     case FilterType::POINT_DIFFUSE:
595     case FilterType::SPOT_DIFFUSE:
596     case FilterType::DISTANT_DIFFUSE:
597     case FilterType::POINT_SPECULAR:
598     case FilterType::SPOT_SPECULAR:
599     case FilterType::DISTANT_SPECULAR:
600       return true;
601     default:
602       return false;
603   }
604 }
605 
606 /* static */
Create(ID2D1DeviceContext * aDC,FilterType aType)607 already_AddRefed<FilterNode> FilterNodeD2D1::Create(ID2D1DeviceContext* aDC,
608                                                     FilterType aType) {
609   if (aType == FilterType::CONVOLVE_MATRIX) {
610     return MakeAndAddRef<FilterNodeConvolveD2D1>(aDC);
611   }
612 
613   RefPtr<ID2D1Effect> effect;
614   HRESULT hr;
615 
616   hr = aDC->CreateEffect(GetCLDIDForFilterType(aType), getter_AddRefs(effect));
617 
618   if (FAILED(hr) || !effect) {
619     gfxCriticalErrorOnce() << "Failed to create effect for FilterType: "
620                            << hexa(hr);
621     return nullptr;
622   }
623 
624   if (aType == FilterType::ARITHMETIC_COMBINE) {
625     effect->SetValue(D2D1_ARITHMETICCOMPOSITE_PROP_CLAMP_OUTPUT, TRUE);
626   }
627 
628   if (aType == FilterType::OPACITY) {
629     return MakeAndAddRef<FilterNodeOpacityD2D1>(effect, aType);
630   }
631 
632   RefPtr<FilterNodeD2D1> filter = new FilterNodeD2D1(effect, aType);
633 
634   if (HasUnboundedOutputRegion(aType)) {
635     // These filters can produce non-transparent output from transparent
636     // input pixels, and we want them to have an unbounded output region.
637     filter = new FilterNodeExtendInputAdapterD2D1(aDC, filter, aType);
638   }
639 
640   if (IsTransferFilterType(aType)) {
641     // Component transfer filters should appear to apply on unpremultiplied
642     // colors, but the D2D1 effects apply on premultiplied colors.
643     filter = new FilterNodePremultiplyAdapterD2D1(aDC, filter, aType);
644   }
645 
646   return filter.forget();
647 }
648 
InitUnmappedProperties()649 void FilterNodeD2D1::InitUnmappedProperties() {
650   switch (mType) {
651     case FilterType::COLOR_MATRIX:
652       mEffect->SetValue(D2D1_COLORMATRIX_PROP_CLAMP_OUTPUT, TRUE);
653       break;
654     case FilterType::TRANSFORM:
655       mEffect->SetValue(D2D1_2DAFFINETRANSFORM_PROP_BORDER_MODE,
656                         D2D1_BORDER_MODE_HARD);
657       break;
658     default:
659       break;
660   }
661 }
662 
SetInput(uint32_t aIndex,SourceSurface * aSurface)663 void FilterNodeD2D1::SetInput(uint32_t aIndex, SourceSurface* aSurface) {
664   UINT32 input = GetD2D1InputForInput(mType, aIndex);
665   ID2D1Effect* effect = InputEffect();
666   MOZ_ASSERT(input < effect->GetInputCount());
667 
668   if (mType == FilterType::COMPOSITE) {
669     UINT32 inputCount = effect->GetInputCount();
670 
671     if (aIndex == inputCount - 1 && aSurface == nullptr) {
672       effect->SetInputCount(inputCount - 1);
673     } else if (aIndex >= inputCount && aSurface) {
674       effect->SetInputCount(aIndex + 1);
675     }
676   }
677 
678   MOZ_ASSERT(input < effect->GetInputCount());
679 
680   mInputSurfaces.resize(effect->GetInputCount());
681   mInputFilters.resize(effect->GetInputCount());
682 
683   // In order to convert aSurface into an ID2D1Image, we need to know what
684   // DrawTarget we paint into. However, the same FilterNode object can be
685   // used on different DrawTargets, so we need to hold on to the SourceSurface
686   // objects and delay the conversion until we're actually painted and know
687   // our target DrawTarget.
688   // The conversion happens in WillDraw().
689 
690   mInputSurfaces[input] = aSurface;
691   mInputFilters[input] = nullptr;
692 
693   // Clear the existing image from the effect.
694   effect->SetInput(input, nullptr);
695 }
696 
SetInput(uint32_t aIndex,FilterNode * aFilter)697 void FilterNodeD2D1::SetInput(uint32_t aIndex, FilterNode* aFilter) {
698   UINT32 input = GetD2D1InputForInput(mType, aIndex);
699   ID2D1Effect* effect = InputEffect();
700 
701   if (mType == FilterType::COMPOSITE) {
702     UINT32 inputCount = effect->GetInputCount();
703 
704     if (aIndex == inputCount - 1 && aFilter == nullptr) {
705       effect->SetInputCount(inputCount - 1);
706     } else if (aIndex >= inputCount && aFilter) {
707       effect->SetInputCount(aIndex + 1);
708     }
709   }
710 
711   MOZ_ASSERT(input < effect->GetInputCount());
712 
713   if (aFilter && aFilter->GetBackendType() != FILTER_BACKEND_DIRECT2D1_1) {
714     gfxWarning() << "Unknown input FilterNode set on effect.";
715     MOZ_ASSERT(0);
716     return;
717   }
718 
719   FilterNodeD2D1* filter = static_cast<FilterNodeD2D1*>(aFilter);
720 
721   mInputSurfaces.resize(effect->GetInputCount());
722   mInputFilters.resize(effect->GetInputCount());
723 
724   // We hold on to the FilterNode object so that we can call WillDraw() on it.
725   mInputSurfaces[input] = nullptr;
726   mInputFilters[input] = filter;
727 
728   if (filter) {
729     effect->SetInputEffect(input, filter->OutputEffect());
730   }
731 }
732 
WillDraw(DrawTarget * aDT)733 void FilterNodeD2D1::WillDraw(DrawTarget* aDT) {
734   // Convert input SourceSurfaces into ID2D1Images and set them on the effect.
735   for (size_t inputIndex = 0; inputIndex < mInputSurfaces.size();
736        inputIndex++) {
737     if (mInputSurfaces[inputIndex]) {
738       ID2D1Effect* effect = InputEffect();
739       RefPtr<ID2D1Image> image =
740           GetImageForSourceSurface(aDT, mInputSurfaces[inputIndex]);
741       effect->SetInput(inputIndex, image);
742     }
743   }
744 
745   // Call WillDraw() on our input filters.
746   for (std::vector<RefPtr<FilterNodeD2D1>>::iterator it = mInputFilters.begin();
747        it != mInputFilters.end(); it++) {
748     if (*it) {
749       (*it)->WillDraw(aDT);
750     }
751   }
752 }
753 
SetAttribute(uint32_t aIndex,uint32_t aValue)754 void FilterNodeD2D1::SetAttribute(uint32_t aIndex, uint32_t aValue) {
755   UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
756   MOZ_ASSERT(input < mEffect->GetPropertyCount());
757 
758   if (mType == FilterType::TURBULENCE &&
759       aIndex == ATT_TURBULENCE_BASE_FREQUENCY) {
760     mEffect->SetValue(input, D2D1::Vector2F(FLOAT(aValue), FLOAT(aValue)));
761     return;
762   } else if (mType == FilterType::DIRECTIONAL_BLUR &&
763              aIndex == ATT_DIRECTIONAL_BLUR_DIRECTION) {
764     mEffect->SetValue(input, aValue == BLUR_DIRECTION_X ? 0 : 90.0f);
765     return;
766   }
767 
768   mEffect->SetValue(input, ConvertValue(mType, aIndex, aValue));
769 }
770 
SetAttribute(uint32_t aIndex,Float aValue)771 void FilterNodeD2D1::SetAttribute(uint32_t aIndex, Float aValue) {
772   UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
773   MOZ_ASSERT(input < mEffect->GetPropertyCount());
774 
775   mEffect->SetValue(input, aValue);
776 }
777 
SetAttribute(uint32_t aIndex,const Point & aValue)778 void FilterNodeD2D1::SetAttribute(uint32_t aIndex, const Point& aValue) {
779   UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
780   MOZ_ASSERT(input < mEffect->GetPropertyCount());
781 
782   mEffect->SetValue(input, D2DPoint(aValue));
783 }
784 
SetAttribute(uint32_t aIndex,const Matrix5x4 & aValue)785 void FilterNodeD2D1::SetAttribute(uint32_t aIndex, const Matrix5x4& aValue) {
786   UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
787   MOZ_ASSERT(input < mEffect->GetPropertyCount());
788 
789   mEffect->SetValue(input, D2DMatrix5x4(aValue));
790 }
791 
SetAttribute(uint32_t aIndex,const Point3D & aValue)792 void FilterNodeD2D1::SetAttribute(uint32_t aIndex, const Point3D& aValue) {
793   UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
794   MOZ_ASSERT(input < mEffect->GetPropertyCount());
795 
796   mEffect->SetValue(input, D2DVector3D(aValue));
797 }
798 
SetAttribute(uint32_t aIndex,const Size & aValue)799 void FilterNodeD2D1::SetAttribute(uint32_t aIndex, const Size& aValue) {
800   UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
801   MOZ_ASSERT(input < mEffect->GetPropertyCount());
802 
803   mEffect->SetValue(input, D2D1::Vector2F(aValue.width, aValue.height));
804 }
805 
SetAttribute(uint32_t aIndex,const IntSize & aValue)806 void FilterNodeD2D1::SetAttribute(uint32_t aIndex, const IntSize& aValue) {
807   UINT32 widthProp, heightProp;
808 
809   if (!GetD2D1PropsForIntSize(mType, aIndex, &widthProp, &heightProp)) {
810     return;
811   }
812 
813   IntSize value = aValue;
814   ConvertValue(mType, aIndex, value);
815 
816   mEffect->SetValue(widthProp, (UINT)value.width);
817   mEffect->SetValue(heightProp, (UINT)value.height);
818 }
819 
SetAttribute(uint32_t aIndex,const DeviceColor & aValue)820 void FilterNodeD2D1::SetAttribute(uint32_t aIndex, const DeviceColor& aValue) {
821   UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
822   MOZ_ASSERT(input < mEffect->GetPropertyCount());
823 
824   switch (mType) {
825     case FilterType::POINT_DIFFUSE:
826     case FilterType::SPOT_DIFFUSE:
827     case FilterType::DISTANT_DIFFUSE:
828     case FilterType::POINT_SPECULAR:
829     case FilterType::SPOT_SPECULAR:
830     case FilterType::DISTANT_SPECULAR:
831       mEffect->SetValue(input, D2D1::Vector3F(aValue.r, aValue.g, aValue.b));
832       break;
833     default:
834       mEffect->SetValue(input,
835                         D2D1::Vector4F(aValue.r * aValue.a, aValue.g * aValue.a,
836                                        aValue.b * aValue.a, aValue.a));
837   }
838 }
839 
SetAttribute(uint32_t aIndex,const Rect & aValue)840 void FilterNodeD2D1::SetAttribute(uint32_t aIndex, const Rect& aValue) {
841   UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
842   MOZ_ASSERT(input < mEffect->GetPropertyCount());
843 
844   mEffect->SetValue(input, D2DRect(aValue));
845 }
846 
SetAttribute(uint32_t aIndex,const IntRect & aValue)847 void FilterNodeD2D1::SetAttribute(uint32_t aIndex, const IntRect& aValue) {
848   if (mType == FilterType::TURBULENCE) {
849     MOZ_ASSERT(aIndex == ATT_TURBULENCE_RECT);
850 
851     mEffect->SetValue(D2D1_TURBULENCE_PROP_OFFSET,
852                       D2D1::Vector2F(Float(aValue.X()), Float(aValue.Y())));
853     mEffect->SetValue(
854         D2D1_TURBULENCE_PROP_SIZE,
855         D2D1::Vector2F(Float(aValue.Width()), Float(aValue.Height())));
856     return;
857   }
858 
859   UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
860   MOZ_ASSERT(input < mEffect->GetPropertyCount());
861 
862   mEffect->SetValue(input,
863                     D2D1::RectF(Float(aValue.X()), Float(aValue.Y()),
864                                 Float(aValue.XMost()), Float(aValue.YMost())));
865 }
866 
SetAttribute(uint32_t aIndex,bool aValue)867 void FilterNodeD2D1::SetAttribute(uint32_t aIndex, bool aValue) {
868   UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
869   MOZ_ASSERT(input < mEffect->GetPropertyCount());
870 
871   mEffect->SetValue(input, (BOOL)aValue);
872 }
873 
SetAttribute(uint32_t aIndex,const Float * aValues,uint32_t aSize)874 void FilterNodeD2D1::SetAttribute(uint32_t aIndex, const Float* aValues,
875                                   uint32_t aSize) {
876   UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
877   MOZ_ASSERT(input < mEffect->GetPropertyCount());
878 
879   mEffect->SetValue(input, (BYTE*)aValues, sizeof(Float) * aSize);
880 }
881 
SetAttribute(uint32_t aIndex,const IntPoint & aValue)882 void FilterNodeD2D1::SetAttribute(uint32_t aIndex, const IntPoint& aValue) {
883   UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
884   MOZ_ASSERT(input < mEffect->GetPropertyCount());
885 
886   mEffect->SetValue(input, D2DPoint(aValue));
887 }
888 
SetAttribute(uint32_t aIndex,const Matrix & aMatrix)889 void FilterNodeD2D1::SetAttribute(uint32_t aIndex, const Matrix& aMatrix) {
890   UINT32 input = GetD2D1PropForAttribute(mType, aIndex);
891   MOZ_ASSERT(input < mEffect->GetPropertyCount());
892 
893   mEffect->SetValue(input, D2DMatrix(aMatrix));
894 }
895 
SetAttribute(uint32_t aIndex,Float aValue)896 void FilterNodeOpacityD2D1::SetAttribute(uint32_t aIndex, Float aValue) {
897   D2D1_MATRIX_5X4_F matrix =
898       D2D1::Matrix5x4F(aValue, 0, 0, 0, 0, aValue, 0, 0, 0, 0, aValue, 0, 0, 0,
899                        0, aValue, 0, 0, 0, 0);
900 
901   mEffect->SetValue(D2D1_COLORMATRIX_PROP_COLOR_MATRIX, matrix);
902   mEffect->SetValue(D2D1_COLORMATRIX_PROP_ALPHA_MODE,
903                     D2D1_COLORMATRIX_ALPHA_MODE_STRAIGHT);
904 }
905 
FilterNodeConvolveD2D1(ID2D1DeviceContext * aDC)906 FilterNodeConvolveD2D1::FilterNodeConvolveD2D1(ID2D1DeviceContext* aDC)
907     : FilterNodeD2D1(nullptr, FilterType::CONVOLVE_MATRIX),
908       mEdgeMode(EDGE_MODE_DUPLICATE) {
909   // Correctly handling the interaction of edge mode and source rect is a bit
910   // tricky with D2D1 effects. We want the edge mode to only apply outside of
911   // the source rect (as specified by the ATT_CONVOLVE_MATRIX_SOURCE_RECT
912   // attribute). So if our input surface or filter is smaller than the source
913   // rect, we need to add transparency around it until we reach the edges of
914   // the source rect, and only then do any repeating or edge duplicating.
915   // Unfortunately, the border effect does not have a source rect attribute -
916   // it only looks at the output rect of its input filter or surface. So we use
917   // our custom ExtendInput effect to adjust the output rect of our input.
918   // All of this is only necessary when our edge mode is not EDGE_MODE_NONE, so
919   // we update the filter chain dynamically in UpdateChain().
920 
921   HRESULT hr;
922 
923   hr = aDC->CreateEffect(CLSID_D2D1ConvolveMatrix, getter_AddRefs(mEffect));
924 
925   if (FAILED(hr) || !mEffect) {
926     gfxWarning() << "Failed to create ConvolveMatrix filter!";
927     return;
928   }
929 
930   mEffect->SetValue(D2D1_CONVOLVEMATRIX_PROP_BORDER_MODE,
931                     D2D1_BORDER_MODE_SOFT);
932 
933   hr = aDC->CreateEffect(CLSID_ExtendInputEffect,
934                          getter_AddRefs(mExtendInputEffect));
935 
936   if (FAILED(hr) || !mExtendInputEffect) {
937     gfxWarning() << "Failed to create ConvolveMatrix filter!";
938     return;
939   }
940 
941   hr = aDC->CreateEffect(CLSID_D2D1Border, getter_AddRefs(mBorderEffect));
942 
943   if (FAILED(hr) || !mBorderEffect) {
944     gfxWarning() << "Failed to create ConvolveMatrix filter!";
945     return;
946   }
947 
948   mBorderEffect->SetInputEffect(0, mExtendInputEffect.get());
949 
950   UpdateChain();
951   UpdateSourceRect();
952 }
953 
SetInput(uint32_t aIndex,FilterNode * aFilter)954 void FilterNodeConvolveD2D1::SetInput(uint32_t aIndex, FilterNode* aFilter) {
955   FilterNodeD2D1::SetInput(aIndex, aFilter);
956 
957   UpdateChain();
958 }
959 
SetAttribute(uint32_t aIndex,uint32_t aValue)960 void FilterNodeConvolveD2D1::SetAttribute(uint32_t aIndex, uint32_t aValue) {
961   if (aIndex != ATT_CONVOLVE_MATRIX_EDGE_MODE) {
962     return FilterNodeD2D1::SetAttribute(aIndex, aValue);
963   }
964 
965   mEdgeMode = (ConvolveMatrixEdgeMode)aValue;
966 
967   UpdateChain();
968 }
969 
InputEffect()970 ID2D1Effect* FilterNodeConvolveD2D1::InputEffect() {
971   return mEdgeMode == EDGE_MODE_NONE ? mEffect.get() : mExtendInputEffect.get();
972 }
973 
UpdateChain()974 void FilterNodeConvolveD2D1::UpdateChain() {
975   // The shape of the filter graph:
976   //
977   // EDGE_MODE_NONE:
978   // input --> convolvematrix
979   //
980   // EDGE_MODE_DUPLICATE or EDGE_MODE_WRAP:
981   // input --> extendinput --> border --> convolvematrix
982   //
983   // mEffect is convolvematrix.
984 
985   if (mEdgeMode != EDGE_MODE_NONE) {
986     mEffect->SetInputEffect(0, mBorderEffect.get());
987   }
988 
989   RefPtr<ID2D1Effect> inputEffect;
990   if (mInputFilters.size() > 0 && mInputFilters[0]) {
991     inputEffect = mInputFilters[0]->OutputEffect();
992   }
993   InputEffect()->SetInputEffect(0, inputEffect);
994 
995   if (mEdgeMode == EDGE_MODE_DUPLICATE) {
996     mBorderEffect->SetValue(D2D1_BORDER_PROP_EDGE_MODE_X,
997                             D2D1_BORDER_EDGE_MODE_CLAMP);
998     mBorderEffect->SetValue(D2D1_BORDER_PROP_EDGE_MODE_Y,
999                             D2D1_BORDER_EDGE_MODE_CLAMP);
1000   } else if (mEdgeMode == EDGE_MODE_WRAP) {
1001     mBorderEffect->SetValue(D2D1_BORDER_PROP_EDGE_MODE_X,
1002                             D2D1_BORDER_EDGE_MODE_WRAP);
1003     mBorderEffect->SetValue(D2D1_BORDER_PROP_EDGE_MODE_Y,
1004                             D2D1_BORDER_EDGE_MODE_WRAP);
1005   }
1006 }
1007 
SetAttribute(uint32_t aIndex,const IntSize & aValue)1008 void FilterNodeConvolveD2D1::SetAttribute(uint32_t aIndex,
1009                                           const IntSize& aValue) {
1010   if (aIndex != ATT_CONVOLVE_MATRIX_KERNEL_SIZE) {
1011     MOZ_ASSERT(false);
1012     return;
1013   }
1014 
1015   mKernelSize = aValue;
1016 
1017   mEffect->SetValue(D2D1_CONVOLVEMATRIX_PROP_KERNEL_SIZE_X, aValue.width);
1018   mEffect->SetValue(D2D1_CONVOLVEMATRIX_PROP_KERNEL_SIZE_Y, aValue.height);
1019 
1020   UpdateOffset();
1021 }
1022 
SetAttribute(uint32_t aIndex,const IntPoint & aValue)1023 void FilterNodeConvolveD2D1::SetAttribute(uint32_t aIndex,
1024                                           const IntPoint& aValue) {
1025   if (aIndex != ATT_CONVOLVE_MATRIX_TARGET) {
1026     MOZ_ASSERT(false);
1027     return;
1028   }
1029 
1030   mTarget = aValue;
1031 
1032   UpdateOffset();
1033 }
1034 
SetAttribute(uint32_t aIndex,const IntRect & aValue)1035 void FilterNodeConvolveD2D1::SetAttribute(uint32_t aIndex,
1036                                           const IntRect& aValue) {
1037   if (aIndex != ATT_CONVOLVE_MATRIX_SOURCE_RECT) {
1038     MOZ_ASSERT(false);
1039     return;
1040   }
1041 
1042   mSourceRect = aValue;
1043 
1044   UpdateSourceRect();
1045 }
1046 
UpdateOffset()1047 void FilterNodeConvolveD2D1::UpdateOffset() {
1048   D2D1_VECTOR_2F vector = D2D1::Vector2F(
1049       (Float(mKernelSize.width) - 1.0f) / 2.0f - Float(mTarget.x),
1050       (Float(mKernelSize.height) - 1.0f) / 2.0f - Float(mTarget.y));
1051 
1052   mEffect->SetValue(D2D1_CONVOLVEMATRIX_PROP_KERNEL_OFFSET, vector);
1053 }
1054 
UpdateSourceRect()1055 void FilterNodeConvolveD2D1::UpdateSourceRect() {
1056   mExtendInputEffect->SetValue(
1057       EXTENDINPUT_PROP_OUTPUT_RECT,
1058       D2D1::Vector4F(Float(mSourceRect.X()), Float(mSourceRect.Y()),
1059                      Float(mSourceRect.XMost()), Float(mSourceRect.YMost())));
1060 }
1061 
FilterNodeExtendInputAdapterD2D1(ID2D1DeviceContext * aDC,FilterNodeD2D1 * aFilterNode,FilterType aType)1062 FilterNodeExtendInputAdapterD2D1::FilterNodeExtendInputAdapterD2D1(
1063     ID2D1DeviceContext* aDC, FilterNodeD2D1* aFilterNode, FilterType aType)
1064     : FilterNodeD2D1(aFilterNode->MainEffect(), aType),
1065       mWrappedFilterNode(aFilterNode) {
1066   // We have an mEffect that looks at the bounds of the input effect, and we
1067   // want mEffect to regard its input as unbounded. So we take the input,
1068   // pipe it through an ExtendInput effect (which has an infinite output rect
1069   // by default), and feed the resulting unbounded composition into mEffect.
1070 
1071   HRESULT hr;
1072 
1073   hr = aDC->CreateEffect(CLSID_ExtendInputEffect,
1074                          getter_AddRefs(mExtendInputEffect));
1075 
1076   if (FAILED(hr) || !mExtendInputEffect) {
1077     gfxWarning() << "Failed to create extend input effect for filter: "
1078                  << hexa(hr);
1079     return;
1080   }
1081 
1082   aFilterNode->InputEffect()->SetInputEffect(0, mExtendInputEffect.get());
1083 }
1084 
FilterNodePremultiplyAdapterD2D1(ID2D1DeviceContext * aDC,FilterNodeD2D1 * aFilterNode,FilterType aType)1085 FilterNodePremultiplyAdapterD2D1::FilterNodePremultiplyAdapterD2D1(
1086     ID2D1DeviceContext* aDC, FilterNodeD2D1* aFilterNode, FilterType aType)
1087     : FilterNodeD2D1(aFilterNode->MainEffect(), aType) {
1088   // D2D1 component transfer effects do strange things when it comes to
1089   // premultiplication.
1090   // For our purposes we only need the transfer filters to apply straight to
1091   // unpremultiplied source channels and output unpremultiplied results.
1092   // However, the D2D1 effects are designed differently: They can apply to both
1093   // premultiplied and unpremultiplied inputs, and they always premultiply
1094   // their result - at least in those color channels that have not been
1095   // disabled.
1096   // In order to determine whether the input needs to be unpremultiplied as
1097   // part of the transfer, the effect consults the alpha mode metadata of the
1098   // input surface or the input effect. We don't have such a concept in Moz2D,
1099   // and giving Moz2D users different results based on something that cannot be
1100   // influenced through Moz2D APIs seems like a bad idea.
1101   // We solve this by applying a premultiply effect to the input before feeding
1102   // it into the transfer effect. The premultiply effect always premultiplies
1103   // regardless of any alpha mode metadata on inputs, and it always marks its
1104   // output as premultiplied so that the transfer effect will unpremultiply
1105   // consistently. Feeding always-premultiplied input into the transfer effect
1106   // also avoids another problem that would appear when individual color
1107   // channels disable the transfer: In that case, the disabled channels would
1108   // pass through unchanged in their unpremultiplied form and the other
1109   // channels would be premultiplied, giving a mixed result.
1110   // But since we now ensure that the input is premultiplied, disabled channels
1111   // will pass premultiplied values through to the result, which is consistent
1112   // with the enabled channels.
1113   // We also add an unpremultiply effect that postprocesses the result of the
1114   // transfer effect because getting unpremultiplied results from the transfer
1115   // filters is part of the FilterNode API.
1116   HRESULT hr;
1117 
1118   hr = aDC->CreateEffect(CLSID_D2D1Premultiply,
1119                          getter_AddRefs(mPrePremultiplyEffect));
1120 
1121   if (FAILED(hr) || !mPrePremultiplyEffect) {
1122     gfxWarning() << "Failed to create ComponentTransfer filter!";
1123     return;
1124   }
1125 
1126   hr = aDC->CreateEffect(CLSID_D2D1UnPremultiply,
1127                          getter_AddRefs(mPostUnpremultiplyEffect));
1128 
1129   if (FAILED(hr) || !mPostUnpremultiplyEffect) {
1130     gfxWarning() << "Failed to create ComponentTransfer filter!";
1131     return;
1132   }
1133 
1134   aFilterNode->InputEffect()->SetInputEffect(0, mPrePremultiplyEffect.get());
1135   mPostUnpremultiplyEffect->SetInputEffect(0, aFilterNode->OutputEffect());
1136 }
1137 
1138 }  // namespace gfx
1139 }  // namespace mozilla
1140