Home | History | Annotate | Download | only in core
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #ifndef SK_CONVOLVER_H
      6 #define SK_CONVOLVER_H
      7 
      8 #include "SkSize.h"
      9 #include "SkTypes.h"
     10 #include "SkTArray.h"
     11 
     12 // avoid confusion with Mac OS X's math library (Carbon)
     13 #if defined(__APPLE__)
     14 #undef FloatToConvolutionFixed
     15 #undef ConvolutionFixedToFloat
     16 #undef FloatToFixed
     17 #undef FixedToFloat
     18 #endif
     19 
     20 // Represents a filter in one dimension. Each output pixel has one entry in this
     21 // object for the filter values contributing to it. You build up the filter
     22 // list by calling AddFilter for each output pixel (in order).
     23 //
     24 // We do 2-dimensional convolution by first convolving each row by one
     25 // SkConvolutionFilter1D, then convolving each column by another one.
     26 //
     27 // Entries are stored in ConvolutionFixed point, shifted left by kShiftBits.
     28 class SkConvolutionFilter1D {
     29 public:
     30     typedef short ConvolutionFixed;
     31 
     32     // The number of bits that ConvolutionFixed point values are shifted by.
     33     enum { kShiftBits = 14 };
     34 
     35     SK_API SkConvolutionFilter1D();
     36     SK_API ~SkConvolutionFilter1D();
     37 
     38     // Convert between floating point and our ConvolutionFixed point representation.
     39     static ConvolutionFixed FloatToFixed(float f) {
     40         return static_cast<ConvolutionFixed>(f * (1 << kShiftBits));
     41     }
     42     static unsigned char FixedToChar(ConvolutionFixed x) {
     43         return static_cast<unsigned char>(x >> kShiftBits);
     44     }
     45     static float FixedToFloat(ConvolutionFixed x) {
     46         // The cast relies on ConvolutionFixed being a short, implying that on
     47         // the platforms we care about all (16) bits will fit into
     48         // the mantissa of a (32-bit) float.
     49         SK_COMPILE_ASSERT(sizeof(ConvolutionFixed) == 2, ConvolutionFixed_type_should_fit_in_float_mantissa);
     50         float raw = static_cast<float>(x);
     51         return ldexpf(raw, -kShiftBits);
     52     }
     53 
     54     // Returns the maximum pixel span of a filter.
     55     int maxFilter() const { return fMaxFilter; }
     56 
     57     // Returns the number of filters in this filter. This is the dimension of the
     58     // output image.
     59     int numValues() const { return static_cast<int>(fFilters.count()); }
     60 
     61     // Appends the given list of scaling values for generating a given output
     62     // pixel. |filterOffset| is the distance from the edge of the image to where
     63     // the scaling factors start. The scaling factors apply to the source pixels
     64     // starting from this position, and going for the next |filterLength| pixels.
     65     //
     66     // You will probably want to make sure your input is normalized (that is,
     67     // all entries in |filterValuesg| sub to one) to prevent affecting the overall
     68     // brighness of the image.
     69     //
     70     // The filterLength must be > 0.
     71     //
     72     // This version will automatically convert your input to ConvolutionFixed point.
     73     SK_API void AddFilter(int filterOffset,
     74                           const float* filterValues,
     75                           int filterLength);
     76 
     77     // Same as the above version, but the input is already ConvolutionFixed point.
     78     void AddFilter(int filterOffset,
     79                    const ConvolutionFixed* filterValues,
     80                    int filterLength);
     81 
     82     // Retrieves a filter for the given |valueOffset|, a position in the output
     83     // image in the direction we're convolving. The offset and length of the
     84     // filter values are put into the corresponding out arguments (see AddFilter
     85     // above for what these mean), and a pointer to the first scaling factor is
     86     // returned. There will be |filterLength| values in this array.
     87     inline const ConvolutionFixed* FilterForValue(int valueOffset,
     88                                        int* filterOffset,
     89                                        int* filterLength) const {
     90         const FilterInstance& filter = fFilters[valueOffset];
     91         *filterOffset = filter.fOffset;
     92         *filterLength = filter.fTrimmedLength;
     93         if (filter.fTrimmedLength == 0) {
     94             return NULL;
     95         }
     96         return &fFilterValues[filter.fDataLocation];
     97     }
     98 
     99   // Retrieves the filter for the offset 0, presumed to be the one and only.
    100   // The offset and length of the filter values are put into the corresponding
    101   // out arguments (see AddFilter). Note that |filterLegth| and
    102   // |specifiedFilterLength| may be different if leading/trailing zeros of the
    103   // original floating point form were clipped.
    104   // There will be |filterLength| values in the return array.
    105   // Returns NULL if the filter is 0-length (for instance when all floating
    106   // point values passed to AddFilter were clipped to 0).
    107     SK_API const ConvolutionFixed* GetSingleFilter(int* specifiedFilterLength,
    108         int* filterOffset,
    109         int* filterLength) const;
    110 
    111     // Add another value to the fFilterValues array -- useful for
    112     // SIMD padding which happens outside of this class.
    113 
    114     void addFilterValue( ConvolutionFixed val ) {
    115         fFilterValues.push_back( val );
    116     }
    117 private:
    118     struct FilterInstance {
    119         // Offset within filterValues for this instance of the filter.
    120         int fDataLocation;
    121 
    122         // Distance from the left of the filter to the center. IN PIXELS
    123         int fOffset;
    124 
    125         // Number of values in this filter instance.
    126         int fTrimmedLength;
    127 
    128         // Filter length as specified. Note that this may be different from
    129         // 'trimmed_length' if leading/trailing zeros of the original floating
    130         // point form were clipped differently on each tail.
    131         int fLength;
    132     };
    133 
    134     // Stores the information for each filter added to this class.
    135     SkTArray<FilterInstance> fFilters;
    136 
    137     // We store all the filter values in this flat list, indexed by
    138     // |FilterInstance.data_location| to avoid the mallocs required for storing
    139     // each one separately.
    140     SkTArray<ConvolutionFixed> fFilterValues;
    141 
    142     // The maximum size of any filter we've added.
    143     int fMaxFilter;
    144 };
    145 
    146 typedef void (*SkConvolveVertically_pointer)(
    147     const SkConvolutionFilter1D::ConvolutionFixed* filterValues,
    148     int filterLength,
    149     unsigned char* const* sourceDataRows,
    150     int pixelWidth,
    151     unsigned char* outRow,
    152     bool hasAlpha);
    153 typedef void (*SkConvolve4RowsHorizontally_pointer)(
    154     const unsigned char* srcData[4],
    155     const SkConvolutionFilter1D& filter,
    156     unsigned char* outRow[4]);
    157 typedef void (*SkConvolveHorizontally_pointer)(
    158     const unsigned char* srcData,
    159     const SkConvolutionFilter1D& filter,
    160     unsigned char* outRow,
    161     bool hasAlpha);
    162 typedef void (*SkConvolveFilterPadding_pointer)(
    163     SkConvolutionFilter1D* filter);
    164 
    165 struct SkConvolutionProcs {
    166   // This is how many extra pixels may be read by the
    167   // conolve*horizontally functions.
    168     int fExtraHorizontalReads;
    169     SkConvolveVertically_pointer fConvolveVertically;
    170     SkConvolve4RowsHorizontally_pointer fConvolve4RowsHorizontally;
    171     SkConvolveHorizontally_pointer fConvolveHorizontally;
    172     SkConvolveFilterPadding_pointer fApplySIMDPadding;
    173 };
    174 
    175 
    176 
    177 // Does a two-dimensional convolution on the given source image.
    178 //
    179 // It is assumed the source pixel offsets referenced in the input filters
    180 // reference only valid pixels, so the source image size is not required. Each
    181 // row of the source image starts |sourceByteRowStride| after the previous
    182 // one (this allows you to have rows with some padding at the end).
    183 //
    184 // The result will be put into the given output buffer. The destination image
    185 // size will be xfilter.numValues() * yfilter.numValues() pixels. It will be
    186 // in rows of exactly xfilter.numValues() * 4 bytes.
    187 //
    188 // |sourceHasAlpha| is a hint that allows us to avoid doing computations on
    189 // the alpha channel if the image is opaque. If you don't know, set this to
    190 // true and it will work properly, but setting this to false will be a few
    191 // percent faster if you know the image is opaque.
    192 //
    193 // The layout in memory is assumed to be 4-bytes per pixel in B-G-R-A order
    194 // (this is ARGB when loaded into 32-bit words on a little-endian machine).
    195 SK_API void BGRAConvolve2D(const unsigned char* sourceData,
    196     int sourceByteRowStride,
    197     bool sourceHasAlpha,
    198     const SkConvolutionFilter1D& xfilter,
    199     const SkConvolutionFilter1D& yfilter,
    200     int outputByteRowStride,
    201     unsigned char* output,
    202     const SkConvolutionProcs&,
    203     bool useSimdIfPossible);
    204 
    205 #endif  // SK_CONVOLVER_H
    206