1138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com// Use of this source code is governed by a BSD-style license that can be
3138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com// found in the LICENSE file.
4138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com
5138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com#ifndef SK_CONVOLVER_H
6138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com#define SK_CONVOLVER_H
7138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com
8138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com#include "SkSize.h"
9138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com#include "SkTypes.h"
10138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com#include "SkTArray.h"
11138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com
12138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com// avoid confusion with Mac OS X's math library (Carbon)
13138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com#if defined(__APPLE__)
14138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com#undef FloatToConvolutionFixed
15138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com#undef ConvolutionFixedToFloat
16ae6a059c68b535e8d2bd814d8d43361e2f8626dfgeorge#undef FloatToFixed
17ae6a059c68b535e8d2bd814d8d43361e2f8626dfgeorge#undef FixedToFloat
18138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com#endif
19138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com
20138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com// Represents a filter in one dimension. Each output pixel has one entry in this
21138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com// object for the filter values contributing to it. You build up the filter
22138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com// list by calling AddFilter for each output pixel (in order).
23138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com//
24138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com// We do 2-dimensional convolution by first convolving each row by one
25138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com// SkConvolutionFilter1D, then convolving each column by another one.
26138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com//
27138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com// Entries are stored in ConvolutionFixed point, shifted left by kShiftBits.
28138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.comclass SkConvolutionFilter1D {
29138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.compublic:
30138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    typedef short ConvolutionFixed;
31138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com
32138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    // The number of bits that ConvolutionFixed point values are shifted by.
33138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    enum { kShiftBits = 14 };
34138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com
35138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    SK_API SkConvolutionFilter1D();
36138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    SK_API ~SkConvolutionFilter1D();
37138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com
38138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    // Convert between floating point and our ConvolutionFixed point representation.
39138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    static ConvolutionFixed FloatToFixed(float f) {
40138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com        return static_cast<ConvolutionFixed>(f * (1 << kShiftBits));
41138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    }
42138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    static unsigned char FixedToChar(ConvolutionFixed x) {
43138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com        return static_cast<unsigned char>(x >> kShiftBits);
44138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    }
45138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    static float FixedToFloat(ConvolutionFixed x) {
46138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com        // The cast relies on ConvolutionFixed being a short, implying that on
47138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com        // the platforms we care about all (16) bits will fit into
48138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com        // the mantissa of a (32-bit) float.
49138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com        SK_COMPILE_ASSERT(sizeof(ConvolutionFixed) == 2, ConvolutionFixed_type_should_fit_in_float_mantissa);
50138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com        float raw = static_cast<float>(x);
51138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com        return ldexpf(raw, -kShiftBits);
52138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    }
53138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com
54138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    // Returns the maximum pixel span of a filter.
55138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    int maxFilter() const { return fMaxFilter; }
56138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com
57138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    // Returns the number of filters in this filter. This is the dimension of the
58138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    // output image.
59138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    int numValues() const { return static_cast<int>(fFilters.count()); }
60138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com
61138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    // Appends the given list of scaling values for generating a given output
62138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    // pixel. |filterOffset| is the distance from the edge of the image to where
63138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    // the scaling factors start. The scaling factors apply to the source pixels
64138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    // starting from this position, and going for the next |filterLength| pixels.
65138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    //
66138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    // You will probably want to make sure your input is normalized (that is,
67138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    // all entries in |filterValuesg| sub to one) to prevent affecting the overall
68138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    // brighness of the image.
69138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    //
70138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    // The filterLength must be > 0.
71138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    //
72138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    // This version will automatically convert your input to ConvolutionFixed point.
73138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    SK_API void AddFilter(int filterOffset,
74138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com                          const float* filterValues,
75138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com                          int filterLength);
76138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com
77138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    // Same as the above version, but the input is already ConvolutionFixed point.
78138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    void AddFilter(int filterOffset,
79138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com                   const ConvolutionFixed* filterValues,
80138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com                   int filterLength);
81138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com
82138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    // Retrieves a filter for the given |valueOffset|, a position in the output
83138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    // image in the direction we're convolving. The offset and length of the
84138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    // filter values are put into the corresponding out arguments (see AddFilter
85138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    // above for what these mean), and a pointer to the first scaling factor is
86138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    // returned. There will be |filterLength| values in this array.
871f3c73825b8a1752abc6b74fbce978a430de6473skia.committer@gmail.com    inline const ConvolutionFixed* FilterForValue(int valueOffset,
88138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com                                       int* filterOffset,
89138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com                                       int* filterLength) const {
90138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com        const FilterInstance& filter = fFilters[valueOffset];
91138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com        *filterOffset = filter.fOffset;
92138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com        *filterLength = filter.fTrimmedLength;
93138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com        if (filter.fTrimmedLength == 0) {
94138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com            return NULL;
95138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com        }
96138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com        return &fFilterValues[filter.fDataLocation];
97138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    }
98138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com
99138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com  // Retrieves the filter for the offset 0, presumed to be the one and only.
100138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com  // The offset and length of the filter values are put into the corresponding
101138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com  // out arguments (see AddFilter). Note that |filterLegth| and
102138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com  // |specifiedFilterLength| may be different if leading/trailing zeros of the
103138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com  // original floating point form were clipped.
104138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com  // There will be |filterLength| values in the return array.
105138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com  // Returns NULL if the filter is 0-length (for instance when all floating
106138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com  // point values passed to AddFilter were clipped to 0).
107138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    SK_API const ConvolutionFixed* GetSingleFilter(int* specifiedFilterLength,
108138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com        int* filterOffset,
109138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com        int* filterLength) const;
110138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com
1111f3c73825b8a1752abc6b74fbce978a430de6473skia.committer@gmail.com    // Add another value to the fFilterValues array -- useful for
112138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    // SIMD padding which happens outside of this class.
1131f3c73825b8a1752abc6b74fbce978a430de6473skia.committer@gmail.com
114138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    void addFilterValue( ConvolutionFixed val ) {
115138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com        fFilterValues.push_back( val );
116138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    }
117138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.comprivate:
118138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    struct FilterInstance {
119138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com        // Offset within filterValues for this instance of the filter.
120138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com        int fDataLocation;
121138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com
122138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com        // Distance from the left of the filter to the center. IN PIXELS
123138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com        int fOffset;
124138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com
125138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com        // Number of values in this filter instance.
126138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com        int fTrimmedLength;
127138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com
128138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com        // Filter length as specified. Note that this may be different from
129138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com        // 'trimmed_length' if leading/trailing zeros of the original floating
130138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com        // point form were clipped differently on each tail.
131138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com        int fLength;
132138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    };
133138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com
134138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    // Stores the information for each filter added to this class.
135138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    SkTArray<FilterInstance> fFilters;
136138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com
137138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    // We store all the filter values in this flat list, indexed by
138138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    // |FilterInstance.data_location| to avoid the mallocs required for storing
139138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    // each one separately.
140138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    SkTArray<ConvolutionFixed> fFilterValues;
141138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com
142138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    // The maximum size of any filter we've added.
143138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    int fMaxFilter;
144138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com};
145138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com
146138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.comtypedef void (*SkConvolveVertically_pointer)(
147138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    const SkConvolutionFilter1D::ConvolutionFixed* filterValues,
148138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    int filterLength,
149138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    unsigned char* const* sourceDataRows,
150138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    int pixelWidth,
151138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    unsigned char* outRow,
152138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    bool hasAlpha);
153138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.comtypedef void (*SkConvolve4RowsHorizontally_pointer)(
154138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    const unsigned char* srcData[4],
155138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    const SkConvolutionFilter1D& filter,
156138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    unsigned char* outRow[4]);
157138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.comtypedef void (*SkConvolveHorizontally_pointer)(
158138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    const unsigned char* srcData,
159138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    const SkConvolutionFilter1D& filter,
160138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    unsigned char* outRow,
161138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    bool hasAlpha);
162138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.comtypedef void (*SkConvolveFilterPadding_pointer)(
163138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    SkConvolutionFilter1D* filter);
164138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com
165138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.comstruct SkConvolutionProcs {
166138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com  // This is how many extra pixels may be read by the
167138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com  // conolve*horizontally functions.
168138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    int fExtraHorizontalReads;
169138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    SkConvolveVertically_pointer fConvolveVertically;
170138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    SkConvolve4RowsHorizontally_pointer fConvolve4RowsHorizontally;
171138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    SkConvolveHorizontally_pointer fConvolveHorizontally;
172138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    SkConvolveFilterPadding_pointer fApplySIMDPadding;
173138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com};
174138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com
175138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com
176138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com
177138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com// Does a two-dimensional convolution on the given source image.
178138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com//
179138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com// It is assumed the source pixel offsets referenced in the input filters
180138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com// reference only valid pixels, so the source image size is not required. Each
181138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com// row of the source image starts |sourceByteRowStride| after the previous
182138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com// one (this allows you to have rows with some padding at the end).
183138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com//
184138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com// The result will be put into the given output buffer. The destination image
185138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com// size will be xfilter.numValues() * yfilter.numValues() pixels. It will be
186138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com// in rows of exactly xfilter.numValues() * 4 bytes.
187138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com//
188138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com// |sourceHasAlpha| is a hint that allows us to avoid doing computations on
189138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com// the alpha channel if the image is opaque. If you don't know, set this to
190138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com// true and it will work properly, but setting this to false will be a few
191138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com// percent faster if you know the image is opaque.
192138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com//
193138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com// The layout in memory is assumed to be 4-bytes per pixel in B-G-R-A order
194138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com// (this is ARGB when loaded into 32-bit words on a little-endian machine).
195138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.comSK_API void BGRAConvolve2D(const unsigned char* sourceData,
196138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    int sourceByteRowStride,
197138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    bool sourceHasAlpha,
198138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    const SkConvolutionFilter1D& xfilter,
199138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    const SkConvolutionFilter1D& yfilter,
200138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    int outputByteRowStride,
201138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    unsigned char* output,
202fed04b34315ed72dbb20e630908638d1c829c760reed@google.com    const SkConvolutionProcs&,
203138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com    bool useSimdIfPossible);
204138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com
205138ebc3e4061cf533ea2f7f3717239670fdc6e43humper@google.com#endif  // SK_CONVOLVER_H
206