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