1 /* 2 * Copyright (C) 2012 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 #include "config.h" 26 27 #include "platform/graphics/filters/SkiaImageFilterBuilder.h" 28 29 #include "SkBlurImageFilter.h" 30 #include "SkColorFilterImageFilter.h" 31 #include "SkColorMatrixFilter.h" 32 #include "SkDropShadowImageFilter.h" 33 #include "SkTableColorFilter.h" 34 #include "platform/graphics/ImageBuffer.h" 35 #include "platform/graphics/filters/FilterEffect.h" 36 #include "platform/graphics/filters/FilterOperations.h" 37 #include "platform/graphics/filters/SourceGraphic.h" 38 #include "public/platform/WebPoint.h" 39 40 namespace { 41 42 PassRefPtr<SkImageFilter> createMatrixImageFilter(SkScalar matrix[20], SkImageFilter* input) 43 { 44 RefPtr<SkColorFilter> colorFilter(adoptRef(new SkColorMatrixFilter(matrix))); 45 return adoptRef(SkColorFilterImageFilter::Create(colorFilter.get(), input)); 46 } 47 48 }; 49 50 namespace WebCore { 51 52 SkiaImageFilterBuilder::SkiaImageFilterBuilder() 53 { 54 } 55 56 SkiaImageFilterBuilder::~SkiaImageFilterBuilder() 57 { 58 } 59 60 PassRefPtr<SkImageFilter> SkiaImageFilterBuilder::build(FilterEffect* effect, ColorSpace colorSpace) 61 { 62 if (!effect) 63 return 0; 64 65 FilterColorSpacePair key(effect, colorSpace); 66 FilterBuilderHashMap::iterator it = m_map.find(key); 67 if (it != m_map.end()) { 68 return it->value; 69 } else { 70 // Note that we may still need the color transform even if the filter is null 71 RefPtr<SkImageFilter> origFilter = effect->createImageFilter(this); 72 RefPtr<SkImageFilter> filter = transformColorSpace(origFilter.get(), effect->operatingColorSpace(), colorSpace); 73 m_map.set(key, filter); 74 return filter.release(); 75 } 76 } 77 78 PassRefPtr<SkImageFilter> SkiaImageFilterBuilder::transformColorSpace( 79 SkImageFilter* input, ColorSpace srcColorSpace, ColorSpace dstColorSpace) { 80 81 RefPtr<SkColorFilter> colorFilter = ImageBuffer::createColorSpaceFilter(srcColorSpace, dstColorSpace); 82 if (!colorFilter) 83 return input; 84 85 return adoptRef(SkColorFilterImageFilter::Create(colorFilter.get(), input)); 86 } 87 88 bool SkiaImageFilterBuilder::buildFilterOperations(const FilterOperations& operations, blink::WebFilterOperations* filters) 89 { 90 if (!filters) 91 return false; 92 93 ColorSpace currentColorSpace = ColorSpaceDeviceRGB; 94 95 RefPtr<SkImageFilter> noopFilter; 96 SkScalar matrix[20]; 97 memset(matrix, 0, 20 * sizeof(SkScalar)); 98 matrix[0] = matrix[6] = matrix[12] = matrix[18] = 1.f; 99 noopFilter = createMatrixImageFilter(matrix, 0); 100 101 for (size_t i = 0; i < operations.size(); ++i) { 102 const FilterOperation& op = *operations.at(i); 103 switch (op.type()) { 104 case FilterOperation::REFERENCE: { 105 RefPtr<SkImageFilter> filter; 106 ReferenceFilter* referenceFilter = toReferenceFilterOperation(op).filter(); 107 if (referenceFilter && referenceFilter->lastEffect()) { 108 FilterEffect* filterEffect = referenceFilter->lastEffect(); 109 // Link SourceGraphic to a noop filter that serves as a placholder for 110 // the previous filter in the chain. We don't know what color space the 111 // interior nodes will request, so we have to populate the map with both 112 // options. (Only one of these will actually have a color transform on it.) 113 FilterColorSpacePair deviceKey(referenceFilter->sourceGraphic(), ColorSpaceDeviceRGB); 114 FilterColorSpacePair linearKey(referenceFilter->sourceGraphic(), ColorSpaceLinearRGB); 115 m_map.set(deviceKey, transformColorSpace(noopFilter.get(), currentColorSpace, ColorSpaceDeviceRGB)); 116 m_map.set(linearKey, transformColorSpace(noopFilter.get(), currentColorSpace, ColorSpaceLinearRGB)); 117 118 currentColorSpace = filterEffect->operatingColorSpace(); 119 filter = SkiaImageFilterBuilder::build(filterEffect, currentColorSpace); 120 // We might have no reference to the SourceGraphic's Skia filter now, so make 121 // sure we don't keep it in the map anymore. 122 m_map.remove(deviceKey); 123 m_map.remove(linearKey); 124 filters->appendReferenceFilter(filter.get()); 125 } 126 break; 127 } 128 case FilterOperation::GRAYSCALE: 129 case FilterOperation::SEPIA: 130 case FilterOperation::SATURATE: 131 case FilterOperation::HUE_ROTATE: { 132 float amount = toBasicColorMatrixFilterOperation(op).amount(); 133 switch (op.type()) { 134 case FilterOperation::GRAYSCALE: 135 filters->appendGrayscaleFilter(amount); 136 break; 137 case FilterOperation::SEPIA: 138 filters->appendSepiaFilter(amount); 139 break; 140 case FilterOperation::SATURATE: 141 filters->appendSaturateFilter(amount); 142 break; 143 case FilterOperation::HUE_ROTATE: 144 filters->appendHueRotateFilter(amount); 145 break; 146 default: 147 ASSERT_NOT_REACHED(); 148 } 149 break; 150 } 151 case FilterOperation::INVERT: 152 case FilterOperation::OPACITY: 153 case FilterOperation::BRIGHTNESS: 154 case FilterOperation::CONTRAST: { 155 float amount = toBasicComponentTransferFilterOperation(op).amount(); 156 switch (op.type()) { 157 case FilterOperation::INVERT: 158 filters->appendInvertFilter(amount); 159 break; 160 case FilterOperation::OPACITY: 161 filters->appendOpacityFilter(amount); 162 break; 163 case FilterOperation::BRIGHTNESS: 164 filters->appendBrightnessFilter(amount); 165 break; 166 case FilterOperation::CONTRAST: 167 filters->appendContrastFilter(amount); 168 break; 169 default: 170 ASSERT_NOT_REACHED(); 171 } 172 break; 173 } 174 case FilterOperation::BLUR: { 175 float pixelRadius = toBlurFilterOperation(op).stdDeviation().getFloatValue(); 176 filters->appendBlurFilter(pixelRadius); 177 break; 178 } 179 case FilterOperation::DROP_SHADOW: { 180 const DropShadowFilterOperation& drop = toDropShadowFilterOperation(op); 181 filters->appendDropShadowFilter(blink::WebPoint(drop.x(), drop.y()), drop.stdDeviation(), drop.color().rgb()); 182 break; 183 } 184 case FilterOperation::VALIDATED_CUSTOM: 185 case FilterOperation::CUSTOM: 186 return false; // Not supported. 187 case FilterOperation::NONE: 188 break; 189 } 190 } 191 if (currentColorSpace != ColorSpaceDeviceRGB) { 192 // Transform to device color space at the end of processing, if required 193 RefPtr<SkImageFilter> filter; 194 filter = transformColorSpace(noopFilter.get(), currentColorSpace, ColorSpaceDeviceRGB); 195 if (filter != noopFilter) 196 filters->appendReferenceFilter(filter.get()); 197 } 198 return true; 199 } 200 201 }; 202