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 "core/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 "core/platform/graphics/ImageBuffer.h" 35 #include "core/platform/graphics/filters/FilterEffect.h" 36 #include "core/platform/graphics/filters/FilterOperations.h" 37 #include "core/platform/graphics/filters/SourceGraphic.h" 38 39 namespace { 40 41 void getBrightnessMatrix(float amount, SkScalar matrix[20]) 42 { 43 memset(matrix, 0, 20 * sizeof(SkScalar)); 44 matrix[0] = matrix[6] = matrix[12] = amount; 45 matrix[18] = 1; 46 } 47 48 void getContrastMatrix(float amount, SkScalar matrix[20]) 49 { 50 memset(matrix, 0, 20 * sizeof(SkScalar)); 51 matrix[0] = matrix[6] = matrix[12] = amount; 52 matrix[4] = matrix[9] = matrix[14] = (-0.5f * amount + 0.5f) * 255; 53 matrix[18] = 1; 54 } 55 56 void getSaturateMatrix(float amount, SkScalar matrix[20]) 57 { 58 // Note, these values are computed to ensure matrixNeedsClamping is false 59 // for amount in [0..1] 60 matrix[0] = 0.213f + 0.787f * amount; 61 matrix[1] = 0.715f - 0.715f * amount; 62 matrix[2] = 1.f - (matrix[0] + matrix[1]); 63 matrix[3] = matrix[4] = 0; 64 matrix[5] = 0.213f - 0.213f * amount; 65 matrix[6] = 0.715f + 0.285f * amount; 66 matrix[7] = 1.f - (matrix[5] + matrix[6]); 67 matrix[8] = matrix[9] = 0; 68 matrix[10] = 0.213f - 0.213f * amount; 69 matrix[11] = 0.715f - 0.715f * amount; 70 matrix[12] = 1.f - (matrix[10] + matrix[11]); 71 matrix[13] = matrix[14] = 0; 72 matrix[15] = matrix[16] = matrix[17] = matrix[19] = 0; 73 matrix[18] = 1; 74 } 75 76 void getHueRotateMatrix(float hue, SkScalar matrix[20]) 77 { 78 float cosHue = cosf(hue * piFloat / 180); 79 float sinHue = sinf(hue * piFloat / 180); 80 matrix[0] = 0.213f + cosHue * 0.787f - sinHue * 0.213f; 81 matrix[1] = 0.715f - cosHue * 0.715f - sinHue * 0.715f; 82 matrix[2] = 0.072f - cosHue * 0.072f + sinHue * 0.928f; 83 matrix[3] = matrix[4] = 0; 84 matrix[5] = 0.213f - cosHue * 0.213f + sinHue * 0.143f; 85 matrix[6] = 0.715f + cosHue * 0.285f + sinHue * 0.140f; 86 matrix[7] = 0.072f - cosHue * 0.072f - sinHue * 0.283f; 87 matrix[8] = matrix[9] = 0; 88 matrix[10] = 0.213f - cosHue * 0.213f - sinHue * 0.787f; 89 matrix[11] = 0.715f - cosHue * 0.715f + sinHue * 0.715f; 90 matrix[12] = 0.072f + cosHue * 0.928f + sinHue * 0.072f; 91 matrix[13] = matrix[14] = 0; 92 matrix[15] = matrix[16] = matrix[17] = 0; 93 matrix[18] = 1; 94 matrix[19] = 0; 95 } 96 97 void getInvertMatrix(float amount, SkScalar matrix[20]) 98 { 99 memset(matrix, 0, 20 * sizeof(SkScalar)); 100 matrix[0] = matrix[6] = matrix[12] = 1 - 2 * amount; 101 matrix[4] = matrix[9] = matrix[14] = amount * 255; 102 matrix[18] = 1; 103 } 104 105 void getOpacityMatrix(float amount, SkScalar matrix[20]) 106 { 107 memset(matrix, 0, 20 * sizeof(SkScalar)); 108 matrix[0] = matrix[6] = matrix[12] = 1; 109 matrix[18] = amount; 110 } 111 112 void getGrayscaleMatrix(float amount, SkScalar matrix[20]) 113 { 114 // Note, these values are computed to ensure matrixNeedsClamping is false 115 // for amount in [0..1] 116 matrix[0] = 0.2126f + 0.7874f * amount; 117 matrix[1] = 0.7152f - 0.7152f * amount; 118 matrix[2] = 1.f - (matrix[0] + matrix[1]); 119 matrix[3] = matrix[4] = 0; 120 121 matrix[5] = 0.2126f - 0.2126f * amount; 122 matrix[6] = 0.7152f + 0.2848f * amount; 123 matrix[7] = 1.f - (matrix[5] + matrix[6]); 124 matrix[8] = matrix[9] = 0; 125 126 matrix[10] = 0.2126f - 0.2126f * amount; 127 matrix[11] = 0.7152f - 0.7152f * amount; 128 matrix[12] = 1.f - (matrix[10] + matrix[11]); 129 matrix[13] = matrix[14] = 0; 130 131 matrix[15] = matrix[16] = matrix[17] = matrix[19] = 0; 132 matrix[18] = 1; 133 } 134 135 void getSepiaMatrix(float amount, SkScalar matrix[20]) 136 { 137 matrix[0] = 0.393f + 0.607f * amount; 138 matrix[1] = 0.769f - 0.769f * amount; 139 matrix[2] = 0.189f - 0.189f * amount; 140 matrix[3] = matrix[4] = 0; 141 142 matrix[5] = 0.349f - 0.349f * amount; 143 matrix[6] = 0.686f + 0.314f * amount; 144 matrix[7] = 0.168f - 0.168f * amount; 145 matrix[8] = matrix[9] = 0; 146 147 matrix[10] = 0.272f - 0.272f * amount; 148 matrix[11] = 0.534f - 0.534f * amount; 149 matrix[12] = 0.131f + 0.869f * amount; 150 matrix[13] = matrix[14] = 0; 151 152 matrix[15] = matrix[16] = matrix[17] = matrix[19] = 0; 153 matrix[18] = 1; 154 } 155 156 PassRefPtr<SkImageFilter> createMatrixImageFilter(SkScalar matrix[20], SkImageFilter* input) 157 { 158 RefPtr<SkColorFilter> colorFilter(adoptRef(new SkColorMatrixFilter(matrix))); 159 return adoptRef(SkColorFilterImageFilter::Create(colorFilter.get(), input)); 160 } 161 162 }; 163 164 namespace WebCore { 165 166 SkiaImageFilterBuilder::SkiaImageFilterBuilder() 167 { 168 } 169 170 SkiaImageFilterBuilder::~SkiaImageFilterBuilder() 171 { 172 } 173 174 PassRefPtr<SkImageFilter> SkiaImageFilterBuilder::build(FilterEffect* effect, ColorSpace colorSpace) 175 { 176 if (!effect) 177 return 0; 178 179 FilterColorSpacePair key(effect, colorSpace); 180 FilterBuilderHashMap::iterator it = m_map.find(key); 181 if (it != m_map.end()) { 182 return it->value; 183 } else { 184 // Note that we may still need the color transform even if the filter is null 185 RefPtr<SkImageFilter> origFilter = effect->createImageFilter(this); 186 RefPtr<SkImageFilter> filter = transformColorSpace(origFilter.get(), effect->operatingColorSpace(), colorSpace); 187 m_map.set(key, filter); 188 return filter.release(); 189 } 190 } 191 192 PassRefPtr<SkImageFilter> SkiaImageFilterBuilder::transformColorSpace( 193 SkImageFilter* input, ColorSpace srcColorSpace, ColorSpace dstColorSpace) { 194 if ((srcColorSpace == dstColorSpace) 195 || (srcColorSpace != ColorSpaceLinearRGB && srcColorSpace != ColorSpaceDeviceRGB) 196 || (dstColorSpace != ColorSpaceLinearRGB && dstColorSpace != ColorSpaceDeviceRGB)) 197 return input; 198 199 const uint8_t* lut = 0; 200 if (dstColorSpace == ColorSpaceLinearRGB) 201 lut = &ImageBuffer::getLinearRgbLUT()[0]; 202 else if (dstColorSpace == ColorSpaceDeviceRGB) 203 lut = &ImageBuffer::getDeviceRgbLUT()[0]; 204 else 205 return input; 206 207 RefPtr<SkColorFilter> colorFilter(adoptRef(SkTableColorFilter::CreateARGB(0, lut, lut, lut))); 208 return adoptRef(SkColorFilterImageFilter::Create(colorFilter.get(), input)); 209 } 210 211 PassRefPtr<SkImageFilter> SkiaImageFilterBuilder::build(const FilterOperations& operations) 212 { 213 RefPtr<SkImageFilter> filter; 214 SkScalar matrix[20]; 215 ColorSpace currentColorSpace = ColorSpaceDeviceRGB; 216 for (size_t i = 0; i < operations.size(); ++i) { 217 const FilterOperation& op = *operations.at(i); 218 switch (op.getOperationType()) { 219 case FilterOperation::REFERENCE: { 220 const ReferenceFilterOperation* referenceFilterOperation = static_cast<const ReferenceFilterOperation*>(&op); 221 ReferenceFilter* referenceFilter = referenceFilterOperation->filter(); 222 if (referenceFilter && referenceFilter->lastEffect()) { 223 FilterEffect* filterEffect = referenceFilter->lastEffect(); 224 // Link SourceGraphic to the previous filter in the chain. 225 // We don't know what color space the interior nodes will request, so we have to populate the map with both options. 226 // (Only one of these will actually have a color transform on it.) 227 FilterColorSpacePair deviceKey(referenceFilter->sourceGraphic(), ColorSpaceDeviceRGB); 228 FilterColorSpacePair linearKey(referenceFilter->sourceGraphic(), ColorSpaceLinearRGB); 229 m_map.set(deviceKey, transformColorSpace(filter.get(), currentColorSpace, ColorSpaceDeviceRGB)); 230 m_map.set(linearKey, transformColorSpace(filter.get(), currentColorSpace, ColorSpaceLinearRGB)); 231 232 currentColorSpace = filterEffect->operatingColorSpace(); 233 filter = SkiaImageFilterBuilder::build(filterEffect, currentColorSpace); 234 // We might have no reference to the SourceGraphic's Skia filter now, so make 235 // sure we don't keep it in the map anymore. 236 m_map.remove(deviceKey); 237 m_map.remove(linearKey); 238 } 239 break; 240 } 241 case FilterOperation::GRAYSCALE: { 242 float amount = static_cast<const BasicColorMatrixFilterOperation*>(&op)->amount(); 243 getGrayscaleMatrix(1 - amount, matrix); 244 filter = createMatrixImageFilter(matrix, filter.get()); 245 break; 246 } 247 case FilterOperation::SEPIA: { 248 float amount = static_cast<const BasicColorMatrixFilterOperation*>(&op)->amount(); 249 getSepiaMatrix(1 - amount, matrix); 250 filter = createMatrixImageFilter(matrix, filter.get()); 251 break; 252 } 253 case FilterOperation::SATURATE: { 254 float amount = static_cast<const BasicColorMatrixFilterOperation*>(&op)->amount(); 255 getSaturateMatrix(amount, matrix); 256 filter = createMatrixImageFilter(matrix, filter.get()); 257 break; 258 } 259 case FilterOperation::HUE_ROTATE: { 260 float amount = static_cast<const BasicColorMatrixFilterOperation*>(&op)->amount(); 261 getHueRotateMatrix(amount, matrix); 262 filter = createMatrixImageFilter(matrix, filter.get()); 263 break; 264 } 265 case FilterOperation::INVERT: { 266 float amount = static_cast<const BasicComponentTransferFilterOperation*>(&op)->amount(); 267 getInvertMatrix(amount, matrix); 268 filter = createMatrixImageFilter(matrix, filter.get()); 269 break; 270 } 271 case FilterOperation::OPACITY: { 272 float amount = static_cast<const BasicComponentTransferFilterOperation*>(&op)->amount(); 273 getOpacityMatrix(amount, matrix); 274 filter = createMatrixImageFilter(matrix, filter.get()); 275 break; 276 } 277 case FilterOperation::BRIGHTNESS: { 278 float amount = static_cast<const BasicComponentTransferFilterOperation*>(&op)->amount(); 279 getBrightnessMatrix(amount, matrix); 280 filter = createMatrixImageFilter(matrix, filter.get()); 281 break; 282 } 283 case FilterOperation::CONTRAST: { 284 float amount = static_cast<const BasicComponentTransferFilterOperation*>(&op)->amount(); 285 getContrastMatrix(amount, matrix); 286 filter = createMatrixImageFilter(matrix, filter.get()); 287 break; 288 } 289 case FilterOperation::BLUR: { 290 float pixelRadius = static_cast<const BlurFilterOperation*>(&op)->stdDeviation().getFloatValue(); 291 filter = adoptRef(new SkBlurImageFilter(pixelRadius, pixelRadius, filter.get())); 292 break; 293 } 294 case FilterOperation::DROP_SHADOW: { 295 const DropShadowFilterOperation* drop = static_cast<const DropShadowFilterOperation*>(&op); 296 filter = adoptRef(new SkDropShadowImageFilter(SkIntToScalar(drop->x()), SkIntToScalar(drop->y()), SkIntToScalar(drop->stdDeviation()), drop->color().rgb(), filter.get())); 297 break; 298 } 299 case FilterOperation::VALIDATED_CUSTOM: 300 case FilterOperation::CUSTOM: 301 // Not supported. 302 case FilterOperation::PASSTHROUGH: 303 case FilterOperation::NONE: 304 break; 305 } 306 } 307 if (currentColorSpace != ColorSpaceDeviceRGB) { 308 // Transform to device color space at the end of processing, if required 309 filter = transformColorSpace(filter.get(), currentColorSpace, ColorSpaceDeviceRGB); 310 } 311 return filter.release(); 312 } 313 314 }; 315