Line data Source code
1 : /* This Source Code Form is subject to the terms of the Mozilla Public
2 : * License, v. 2.0. If a copy of the MPL was not distributed with this
3 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 :
5 : #include "2D.h"
6 : #include "ConvolutionFilter.h"
7 : #include "skia/src/core/SkBitmapFilter.h"
8 : #include "skia/src/core/SkConvolver.h"
9 : #include "skia/src/core/SkOpts.h"
10 : #include <algorithm>
11 : #include <cmath>
12 : #include "mozilla/Vector.h"
13 :
14 : namespace mozilla {
15 : namespace gfx {
16 :
17 0 : ConvolutionFilter::ConvolutionFilter()
18 0 : : mFilter(MakeUnique<SkConvolutionFilter1D>())
19 : {
20 0 : }
21 :
22 0 : ConvolutionFilter::~ConvolutionFilter()
23 : {
24 0 : }
25 :
26 : int32_t
27 0 : ConvolutionFilter::MaxFilter() const
28 : {
29 0 : return mFilter->maxFilter();
30 : }
31 :
32 : int32_t
33 0 : ConvolutionFilter::NumValues() const
34 : {
35 0 : return mFilter->numValues();
36 : }
37 :
38 : bool
39 0 : ConvolutionFilter::GetFilterOffsetAndLength(int32_t aRowIndex, int32_t* aResultOffset, int32_t* aResultLength)
40 : {
41 0 : if (aRowIndex >= mFilter->numValues()) {
42 0 : return false;
43 : }
44 0 : mFilter->FilterForValue(aRowIndex, aResultOffset, aResultLength);
45 0 : return true;
46 : }
47 :
48 : void
49 0 : ConvolutionFilter::ConvolveHorizontally(const uint8_t* aSrc, uint8_t* aDst, bool aHasAlpha)
50 : {
51 0 : SkOpts::convolve_horizontally(aSrc, *mFilter, aDst, aHasAlpha);
52 0 : }
53 :
54 : void
55 0 : ConvolutionFilter::ConvolveVertically(uint8_t* const* aSrc, uint8_t* aDst, int32_t aRowIndex, int32_t aRowSize, bool aHasAlpha)
56 : {
57 0 : MOZ_ASSERT(aRowIndex < mFilter->numValues());
58 :
59 : int32_t filterOffset;
60 : int32_t filterLength;
61 0 : auto filterValues = mFilter->FilterForValue(aRowIndex, &filterOffset, &filterLength);
62 0 : SkOpts::convolve_vertically(filterValues, filterLength, aSrc, aRowSize, aDst, aHasAlpha);
63 0 : }
64 :
65 : /* ConvolutionFilter::ComputeResizeFactor is derived from Skia's SkBitmapScaler/SkResizeFilter::computeFactors.
66 : * It is governed by Skia's BSD-style license (see gfx/skia/LICENSE) and the following copyright:
67 : * Copyright (c) 2015 Google Inc.
68 : */
69 : bool
70 0 : ConvolutionFilter::ComputeResizeFilter(ResizeMethod aResizeMethod, int32_t aSrcSize, int32_t aDstSize)
71 : {
72 : typedef SkConvolutionFilter1D::ConvolutionFixed Fixed;
73 :
74 0 : UniquePtr<SkBitmapFilter> bitmapFilter;
75 0 : switch (aResizeMethod) {
76 : case ResizeMethod::BOX:
77 0 : bitmapFilter = MakeUnique<SkBoxFilter>();
78 0 : break;
79 : case ResizeMethod::TRIANGLE:
80 0 : bitmapFilter = MakeUnique<SkTriangleFilter>();
81 0 : break;
82 : case ResizeMethod::LANCZOS3:
83 0 : bitmapFilter = MakeUnique<SkLanczosFilter>();
84 0 : break;
85 : case ResizeMethod::HAMMING:
86 0 : bitmapFilter = MakeUnique<SkHammingFilter>();
87 0 : break;
88 : case ResizeMethod::MITCHELL:
89 0 : bitmapFilter = MakeUnique<SkMitchellFilter>();
90 0 : break;
91 : default:
92 0 : return false;
93 : }
94 :
95 : // When we're doing a magnification, the scale will be larger than one. This
96 : // means the destination pixels are much smaller than the source pixels, and
97 : // that the range covered by the filter won't necessarily cover any source
98 : // pixel boundaries. Therefore, we use these clamped values (max of 1) for
99 : // some computations.
100 0 : float scale = float(aDstSize) / float(aSrcSize);
101 0 : float clampedScale = std::min(1.0f, scale);
102 : // This is how many source pixels from the center we need to count
103 : // to support the filtering function.
104 0 : float srcSupport = bitmapFilter->width() / clampedScale;
105 0 : float invScale = 1.0f / scale;
106 :
107 0 : Vector<float, 64> filterValues;
108 0 : Vector<Fixed, 64> fixedFilterValues;
109 :
110 : // Loop over all pixels in the output range. We will generate one set of
111 : // filter values for each one. Those values will tell us how to blend the
112 : // source pixels to compute the destination pixel.
113 :
114 : // This is the pixel in the source directly under the pixel in the dest.
115 : // Note that we base computations on the "center" of the pixels. To see
116 : // why, observe that the destination pixel at coordinates (0, 0) in a 5.0x
117 : // downscale should "cover" the pixels around the pixel with *its center*
118 : // at coordinates (2.5, 2.5) in the source, not those around (0, 0).
119 : // Hence we need to scale coordinates (0.5, 0.5), not (0, 0).
120 0 : float srcPixel = 0.5f * invScale;
121 0 : mFilter->reserveAdditional(aDstSize, int32_t(ceil(aDstSize * srcSupport * 2)));
122 0 : for (int32_t destI = 0; destI < aDstSize; destI++) {
123 : // Compute the (inclusive) range of source pixels the filter covers.
124 0 : float srcBegin = std::max(0.0f, floorf(srcPixel - srcSupport));
125 0 : float srcEnd = std::min(aSrcSize - 1.0f, ceilf(srcPixel + srcSupport));
126 :
127 : // Compute the unnormalized filter value at each location of the source
128 : // it covers.
129 :
130 : // Sum of the filter values for normalizing.
131 : // Distance from the center of the filter, this is the filter coordinate
132 : // in source space. We also need to consider the center of the pixel
133 : // when comparing distance against 'srcPixel'. In the 5x downscale
134 : // example used above the distance from the center of the filter to
135 : // the pixel with coordinates (2, 2) should be 0, because its center
136 : // is at (2.5, 2.5).
137 0 : float destFilterDist = (srcBegin + 0.5f - srcPixel) * clampedScale;
138 0 : int32_t filterCount = int32_t(srcEnd - srcBegin) + 1;
139 0 : if (filterCount <= 0 ||
140 0 : !filterValues.resize(filterCount) ||
141 0 : !fixedFilterValues.resize(filterCount)) {
142 0 : return false;
143 : }
144 0 : float filterSum = bitmapFilter->evaluate_n(destFilterDist, clampedScale, filterCount,
145 0 : filterValues.begin());
146 :
147 : // The filter must be normalized so that we don't affect the brightness of
148 : // the image. Convert to normalized fixed point.
149 0 : Fixed fixedSum = 0;
150 0 : float invFilterSum = 1.0f / filterSum;
151 0 : for (int32_t fixedI = 0; fixedI < filterCount; fixedI++) {
152 0 : Fixed curFixed = SkConvolutionFilter1D::FloatToFixed(filterValues[fixedI] * invFilterSum);
153 0 : fixedSum += curFixed;
154 0 : fixedFilterValues[fixedI] = curFixed;
155 : }
156 :
157 : // The conversion to fixed point will leave some rounding errors, which
158 : // we add back in to avoid affecting the brightness of the image. We
159 : // arbitrarily add this to the center of the filter array (this won't always
160 : // be the center of the filter function since it could get clipped on the
161 : // edges, but it doesn't matter enough to worry about that case).
162 0 : Fixed leftovers = SkConvolutionFilter1D::FloatToFixed(1) - fixedSum;
163 0 : fixedFilterValues[filterCount / 2] += leftovers;
164 :
165 0 : mFilter->AddFilter(int32_t(srcBegin), fixedFilterValues.begin(), filterCount);
166 :
167 0 : srcPixel += invScale;
168 : }
169 :
170 0 : return mFilter->maxFilter() > 0 && mFilter->numValues() == aDstSize;
171 : }
172 :
173 : } // namespace gfx
174 : } // namespace mozilla
175 :
|