Home | History | Annotate | Download | only in filters
      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