Home | History | Annotate | Download | only in filters
      1 /*
      2  * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann (at) kde.org>
      3  * Copyright (C) 2004, 2005 Rob Buis <buis (at) kde.org>
      4  * Copyright (C) 2005 Eric Seidel <eric (at) webkit.org>
      5  * Copyright (C) 2009 Dirk Schulze <krit (at) webkit.org>
      6  * Copyright (C) Research In Motion Limited 2010. All rights reserved.
      7  * Copyright (C) 2013 Google Inc. All rights reserved.
      8  *
      9  * This library is free software; you can redistribute it and/or
     10  * modify it under the terms of the GNU Library General Public
     11  * License as published by the Free Software Foundation; either
     12  * version 2 of the License, or (at your option) any later version.
     13  *
     14  * This library is distributed in the hope that it will be useful,
     15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     17  * Library General Public License for more details.
     18  *
     19  * You should have received a copy of the GNU Library General Public License
     20  * along with this library; see the file COPYING.LIB.  If not, write to
     21  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     22  * Boston, MA 02110-1301, USA.
     23  */
     24 
     25 #include "config.h"
     26 
     27 #include "platform/graphics/filters/FEComposite.h"
     28 
     29 #include "SkArithmeticMode.h"
     30 #include "SkXfermodeImageFilter.h"
     31 
     32 #include "platform/graphics/GraphicsContext.h"
     33 #include "platform/graphics/cpu/arm/filters/FECompositeArithmeticNEON.h"
     34 #include "platform/graphics/filters/SkiaImageFilterBuilder.h"
     35 #include "platform/text/TextStream.h"
     36 #include "third_party/skia/include/core/SkDevice.h"
     37 
     38 #include "wtf/Uint8ClampedArray.h"
     39 
     40 namespace blink {
     41 
     42 FEComposite::FEComposite(Filter* filter, const CompositeOperationType& type, float k1, float k2, float k3, float k4)
     43     : FilterEffect(filter)
     44     , m_type(type)
     45     , m_k1(k1)
     46     , m_k2(k2)
     47     , m_k3(k3)
     48     , m_k4(k4)
     49 {
     50 }
     51 
     52 PassRefPtr<FEComposite> FEComposite::create(Filter* filter, const CompositeOperationType& type, float k1, float k2, float k3, float k4)
     53 {
     54     return adoptRef(new FEComposite(filter, type, k1, k2, k3, k4));
     55 }
     56 
     57 CompositeOperationType FEComposite::operation() const
     58 {
     59     return m_type;
     60 }
     61 
     62 bool FEComposite::setOperation(CompositeOperationType type)
     63 {
     64     if (m_type == type)
     65         return false;
     66     m_type = type;
     67     return true;
     68 }
     69 
     70 float FEComposite::k1() const
     71 {
     72     return m_k1;
     73 }
     74 
     75 bool FEComposite::setK1(float k1)
     76 {
     77     if (m_k1 == k1)
     78         return false;
     79     m_k1 = k1;
     80     return true;
     81 }
     82 
     83 float FEComposite::k2() const
     84 {
     85     return m_k2;
     86 }
     87 
     88 bool FEComposite::setK2(float k2)
     89 {
     90     if (m_k2 == k2)
     91         return false;
     92     m_k2 = k2;
     93     return true;
     94 }
     95 
     96 float FEComposite::k3() const
     97 {
     98     return m_k3;
     99 }
    100 
    101 bool FEComposite::setK3(float k3)
    102 {
    103     if (m_k3 == k3)
    104         return false;
    105     m_k3 = k3;
    106     return true;
    107 }
    108 
    109 float FEComposite::k4() const
    110 {
    111     return m_k4;
    112 }
    113 
    114 bool FEComposite::setK4(float k4)
    115 {
    116     if (m_k4 == k4)
    117         return false;
    118     m_k4 = k4;
    119     return true;
    120 }
    121 
    122 void FEComposite::correctFilterResultIfNeeded()
    123 {
    124     if (m_type != FECOMPOSITE_OPERATOR_ARITHMETIC)
    125         return;
    126 
    127     forceValidPreMultipliedPixels();
    128 }
    129 
    130 template <int b1, int b4>
    131 static inline void computeArithmeticPixels(unsigned char* source, unsigned char* destination, int pixelArrayLength,
    132                                     float k1, float k2, float k3, float k4)
    133 {
    134     float scaledK1;
    135     float scaledK4;
    136     if (b1)
    137         scaledK1 = k1 / 255.0f;
    138     if (b4)
    139         scaledK4 = k4 * 255.0f;
    140 
    141     while (--pixelArrayLength >= 0) {
    142         unsigned char i1 = *source;
    143         unsigned char i2 = *destination;
    144         float result = k2 * i1 + k3 * i2;
    145         if (b1)
    146             result += scaledK1 * i1 * i2;
    147         if (b4)
    148             result += scaledK4;
    149 
    150         if (result <= 0)
    151             *destination = 0;
    152         else if (result >= 255)
    153             *destination = 255;
    154         else
    155             *destination = result;
    156         ++source;
    157         ++destination;
    158     }
    159 }
    160 
    161 // computeArithmeticPixelsUnclamped is a faster version of computeArithmeticPixels for the common case where clamping
    162 // is not necessary. This enables aggresive compiler optimizations such as auto-vectorization.
    163 template <int b1, int b4>
    164 static inline void computeArithmeticPixelsUnclamped(unsigned char* source, unsigned char* destination, int pixelArrayLength, float k1, float k2, float k3, float k4)
    165 {
    166     float scaledK1;
    167     float scaledK4;
    168     if (b1)
    169         scaledK1 = k1 / 255.0f;
    170     if (b4)
    171         scaledK4 = k4 * 255.0f;
    172 
    173     while (--pixelArrayLength >= 0) {
    174         unsigned char i1 = *source;
    175         unsigned char i2 = *destination;
    176         float result = k2 * i1 + k3 * i2;
    177         if (b1)
    178             result += scaledK1 * i1 * i2;
    179         if (b4)
    180             result += scaledK4;
    181 
    182         *destination = result;
    183         ++source;
    184         ++destination;
    185     }
    186 }
    187 
    188 static inline void arithmeticSoftware(unsigned char* source, unsigned char* destination, int pixelArrayLength, float k1, float k2, float k3, float k4)
    189 {
    190     float upperLimit = std::max(0.0f, k1) + std::max(0.0f, k2) + std::max(0.0f, k3) + k4;
    191     float lowerLimit = std::min(0.0f, k1) + std::min(0.0f, k2) + std::min(0.0f, k3) + k4;
    192     if ((k4 >= 0.0f && k4 <= 1.0f) && (upperLimit >= 0.0f && upperLimit <= 1.0f) && (lowerLimit >= 0.0f && lowerLimit <= 1.0f)) {
    193         if (k4) {
    194             if (k1)
    195                 computeArithmeticPixelsUnclamped<1, 1>(source, destination, pixelArrayLength, k1, k2, k3, k4);
    196             else
    197                 computeArithmeticPixelsUnclamped<0, 1>(source, destination, pixelArrayLength, k1, k2, k3, k4);
    198         } else {
    199             if (k1)
    200                 computeArithmeticPixelsUnclamped<1, 0>(source, destination, pixelArrayLength, k1, k2, k3, k4);
    201             else
    202                 computeArithmeticPixelsUnclamped<0, 0>(source, destination, pixelArrayLength, k1, k2, k3, k4);
    203         }
    204         return;
    205     }
    206 
    207     if (k4) {
    208         if (k1)
    209             computeArithmeticPixels<1, 1>(source, destination, pixelArrayLength, k1, k2, k3, k4);
    210         else
    211             computeArithmeticPixels<0, 1>(source, destination, pixelArrayLength, k1, k2, k3, k4);
    212     } else {
    213         if (k1)
    214             computeArithmeticPixels<1, 0>(source, destination, pixelArrayLength, k1, k2, k3, k4);
    215         else
    216             computeArithmeticPixels<0, 0>(source, destination, pixelArrayLength, k1, k2, k3, k4);
    217     }
    218 }
    219 
    220 inline void FEComposite::platformArithmeticSoftware(Uint8ClampedArray* source, Uint8ClampedArray* destination,
    221     float k1, float k2, float k3, float k4)
    222 {
    223     int length = source->length();
    224     ASSERT(length == static_cast<int>(destination->length()));
    225     // The selection here eventually should happen dynamically.
    226 #if HAVE(ARM_NEON_INTRINSICS)
    227     ASSERT(!(length & 0x3));
    228     platformArithmeticNeon(source->data(), destination->data(), length, k1, k2, k3, k4);
    229 #else
    230     arithmeticSoftware(source->data(), destination->data(), length, k1, k2, k3, k4);
    231 #endif
    232 }
    233 
    234 FloatRect FEComposite::determineAbsolutePaintRect(const FloatRect& originalRequestedRect)
    235 {
    236     FloatRect requestedRect = originalRequestedRect;
    237     if (clipsToBounds())
    238         requestedRect.intersect(maxEffectRect());
    239 
    240     // We may be called multiple times if result is used more than once. Return
    241     // quickly if nothing new is required.
    242     if (absolutePaintRect().contains(enclosingIntRect(requestedRect)))
    243         return requestedRect;
    244 
    245     // No mapPaintRect required for FEComposite.
    246     FloatRect input1Rect = inputEffect(1)->determineAbsolutePaintRect(requestedRect);
    247     FloatRect affectedRect;
    248     switch (m_type) {
    249     case FECOMPOSITE_OPERATOR_IN:
    250         // 'in' has output only in the intersection of both inputs.
    251         affectedRect = intersection(input1Rect, inputEffect(0)->determineAbsolutePaintRect(input1Rect));
    252         break;
    253     case FECOMPOSITE_OPERATOR_ATOP:
    254         // 'atop' has output only in the extents of the second input.
    255         // Make sure first input knows where it needs to produce output.
    256         inputEffect(0)->determineAbsolutePaintRect(input1Rect);
    257         affectedRect = input1Rect;
    258         break;
    259     case FECOMPOSITE_OPERATOR_ARITHMETIC:
    260         if (k4() > 0) {
    261             // Make sure first input knows where it needs to produce output.
    262             inputEffect(0)->determineAbsolutePaintRect(requestedRect);
    263             // Arithmetic with non-zero k4 may influnce the complete filter primitive
    264             // region. So we can't optimize the paint region here.
    265             affectedRect = requestedRect;
    266             break;
    267         }
    268         if (k2() <= 0) {
    269             // Input 0 does not appear where input 1 is not present.
    270             FloatRect input0Rect = inputEffect(0)->determineAbsolutePaintRect(input1Rect);
    271             if (k3() > 0) {
    272                 affectedRect = input1Rect;
    273             } else {
    274                 // Just k1 is positive. Use intersection.
    275                 affectedRect = intersection(input1Rect, input0Rect);
    276             }
    277             break;
    278         }
    279         // else fall through to use union
    280     default:
    281         // Take the union of both input effects.
    282         affectedRect = unionRect(input1Rect, inputEffect(0)->determineAbsolutePaintRect(requestedRect));
    283         break;
    284     }
    285 
    286     affectedRect.intersect(requestedRect);
    287     addAbsolutePaintRect(affectedRect);
    288     return affectedRect;
    289 }
    290 
    291 void FEComposite::applySoftware()
    292 {
    293     FilterEffect* in = inputEffect(0);
    294     FilterEffect* in2 = inputEffect(1);
    295 
    296     if (m_type == FECOMPOSITE_OPERATOR_ARITHMETIC) {
    297         Uint8ClampedArray* dstPixelArray = createPremultipliedImageResult();
    298         if (!dstPixelArray)
    299             return;
    300 
    301         IntRect effectADrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect());
    302         RefPtr<Uint8ClampedArray> srcPixelArray = in->asPremultipliedImage(effectADrawingRect);
    303 
    304         IntRect effectBDrawingRect = requestedRegionOfInputImageData(in2->absolutePaintRect());
    305         in2->copyPremultipliedImage(dstPixelArray, effectBDrawingRect);
    306 
    307         platformArithmeticSoftware(srcPixelArray.get(), dstPixelArray, m_k1, m_k2, m_k3, m_k4);
    308         return;
    309     }
    310 
    311     ImageBuffer* resultImage = createImageBufferResult();
    312     if (!resultImage)
    313         return;
    314     GraphicsContext* filterContext = resultImage->context();
    315 
    316     ImageBuffer* imageBuffer = in->asImageBuffer();
    317     ImageBuffer* imageBuffer2 = in2->asImageBuffer();
    318     ASSERT(imageBuffer);
    319     ASSERT(imageBuffer2);
    320 
    321     switch (m_type) {
    322     case FECOMPOSITE_OPERATOR_OVER:
    323         filterContext->drawImageBuffer(imageBuffer2, drawingRegionOfInputImage(in2->absolutePaintRect()));
    324         filterContext->drawImageBuffer(imageBuffer, drawingRegionOfInputImage(in->absolutePaintRect()));
    325         break;
    326     case FECOMPOSITE_OPERATOR_IN: {
    327         // Applies only to the intersected region.
    328         IntRect destinationRect = in->absolutePaintRect();
    329         destinationRect.intersect(in2->absolutePaintRect());
    330         destinationRect.intersect(absolutePaintRect());
    331         if (destinationRect.isEmpty())
    332             break;
    333         FloatRect sourceRect(IntPoint(destinationRect.x() - in->absolutePaintRect().x(),
    334                                     destinationRect.y() - in->absolutePaintRect().y()), destinationRect.size());
    335         FloatRect source2Rect(IntPoint(destinationRect.x() - in2->absolutePaintRect().x(),
    336                                      destinationRect.y() - in2->absolutePaintRect().y()), destinationRect.size());
    337         destinationRect.move(-absolutePaintRect().x(), -absolutePaintRect().y());
    338         filterContext->drawImageBuffer(imageBuffer2, destinationRect, &source2Rect);
    339         filterContext->drawImageBuffer(imageBuffer, destinationRect, &sourceRect, CompositeSourceIn);
    340         break;
    341     }
    342     case FECOMPOSITE_OPERATOR_OUT:
    343         filterContext->drawImageBuffer(imageBuffer, drawingRegionOfInputImage(in->absolutePaintRect()));
    344         filterContext->drawImageBuffer(imageBuffer2, drawingRegionOfInputImage(in2->absolutePaintRect()), 0, CompositeDestinationOut);
    345         break;
    346     case FECOMPOSITE_OPERATOR_ATOP:
    347         filterContext->drawImageBuffer(imageBuffer2, drawingRegionOfInputImage(in2->absolutePaintRect()));
    348         filterContext->drawImageBuffer(imageBuffer, drawingRegionOfInputImage(in->absolutePaintRect()), 0, CompositeSourceAtop);
    349         break;
    350     case FECOMPOSITE_OPERATOR_XOR:
    351         filterContext->drawImageBuffer(imageBuffer2, drawingRegionOfInputImage(in2->absolutePaintRect()));
    352         filterContext->drawImageBuffer(imageBuffer, drawingRegionOfInputImage(in->absolutePaintRect()), 0, CompositeXOR);
    353         break;
    354     default:
    355         break;
    356     }
    357 }
    358 
    359 SkXfermode::Mode toXfermode(CompositeOperationType mode)
    360 {
    361     switch (mode) {
    362     case FECOMPOSITE_OPERATOR_OVER:
    363         return SkXfermode::kSrcOver_Mode;
    364     case FECOMPOSITE_OPERATOR_IN:
    365         return SkXfermode::kSrcIn_Mode;
    366     case FECOMPOSITE_OPERATOR_OUT:
    367         return SkXfermode::kSrcOut_Mode;
    368     case FECOMPOSITE_OPERATOR_ATOP:
    369         return SkXfermode::kSrcATop_Mode;
    370     case FECOMPOSITE_OPERATOR_XOR:
    371         return SkXfermode::kXor_Mode;
    372     default:
    373         ASSERT_NOT_REACHED();
    374         return SkXfermode::kSrcOver_Mode;
    375     }
    376 }
    377 
    378 PassRefPtr<SkImageFilter> FEComposite::createImageFilter(SkiaImageFilterBuilder* builder)
    379 {
    380     return createImageFilterInternal(builder, true);
    381 }
    382 
    383 PassRefPtr<SkImageFilter> FEComposite::createImageFilterWithoutValidation(SkiaImageFilterBuilder* builder)
    384 {
    385     return createImageFilterInternal(builder, false);
    386 }
    387 
    388 PassRefPtr<SkImageFilter> FEComposite::createImageFilterInternal(SkiaImageFilterBuilder* builder, bool requiresPMColorValidation)
    389 {
    390     RefPtr<SkImageFilter> foreground(builder->build(inputEffect(0), operatingColorSpace(), !mayProduceInvalidPreMultipliedPixels()));
    391     RefPtr<SkImageFilter> background(builder->build(inputEffect(1), operatingColorSpace(), !mayProduceInvalidPreMultipliedPixels()));
    392     SkImageFilter::CropRect cropRect = getCropRect(builder->cropOffset());
    393     RefPtr<SkXfermode> mode;
    394     if (m_type == FECOMPOSITE_OPERATOR_ARITHMETIC)
    395         mode = adoptRef(SkArithmeticMode::Create(SkFloatToScalar(m_k1), SkFloatToScalar(m_k2), SkFloatToScalar(m_k3), SkFloatToScalar(m_k4), requiresPMColorValidation));
    396     else
    397         mode = adoptRef(SkXfermode::Create(toXfermode(m_type)));
    398     return adoptRef(SkXfermodeImageFilter::Create(mode.get(), background.get(), foreground.get(), &cropRect));
    399 }
    400 
    401 static TextStream& operator<<(TextStream& ts, const CompositeOperationType& type)
    402 {
    403     switch (type) {
    404     case FECOMPOSITE_OPERATOR_UNKNOWN:
    405         ts << "UNKNOWN";
    406         break;
    407     case FECOMPOSITE_OPERATOR_OVER:
    408         ts << "OVER";
    409         break;
    410     case FECOMPOSITE_OPERATOR_IN:
    411         ts << "IN";
    412         break;
    413     case FECOMPOSITE_OPERATOR_OUT:
    414         ts << "OUT";
    415         break;
    416     case FECOMPOSITE_OPERATOR_ATOP:
    417         ts << "ATOP";
    418         break;
    419     case FECOMPOSITE_OPERATOR_XOR:
    420         ts << "XOR";
    421         break;
    422     case FECOMPOSITE_OPERATOR_ARITHMETIC:
    423         ts << "ARITHMETIC";
    424         break;
    425     }
    426     return ts;
    427 }
    428 
    429 TextStream& FEComposite::externalRepresentation(TextStream& ts, int indent) const
    430 {
    431     writeIndent(ts, indent);
    432     ts << "[feComposite";
    433     FilterEffect::externalRepresentation(ts);
    434     ts << " operation=\"" << m_type << "\"";
    435     if (m_type == FECOMPOSITE_OPERATOR_ARITHMETIC)
    436         ts << " k1=\"" << m_k1 << "\" k2=\"" << m_k2 << "\" k3=\"" << m_k3 << "\" k4=\"" << m_k4 << "\"";
    437     ts << "]\n";
    438     inputEffect(0)->externalRepresentation(ts, indent + 1);
    439     inputEffect(1)->externalRepresentation(ts, indent + 1);
    440     return ts;
    441 }
    442 
    443 } // namespace blink
    444