Source code
Revision control
Copy as Markdown
Other Tools
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2011-2016 Google Inc.
// Use of this source code is governed by a BSD-style license that can be
// found in the gfx/skia/LICENSE file.
#ifndef MOZILLA_GFX_SKCONVOLVER_H_
#define MOZILLA_GFX_SKCONVOLVER_H_
#include "mozilla/Assertions.h"
#include <cfloat>
#include <cmath>
#include "mozilla/Vector.h"
namespace skia {
class SkBitmapFilter {
public:
explicit SkBitmapFilter(float width) : fWidth(width) {}
virtual ~SkBitmapFilter() = default;
float width() const { return fWidth; }
virtual float evaluate(float x) const = 0;
protected:
float fWidth;
};
class SkBoxFilter final : public SkBitmapFilter {
public:
explicit SkBoxFilter(float width = 0.5f) : SkBitmapFilter(width) {}
float evaluate(float x) const override {
return (x >= -fWidth && x < fWidth) ? 1.0f : 0.0f;
}
};
class SkLanczosFilter final : public SkBitmapFilter {
public:
explicit SkLanczosFilter(float width = 3.0f) : SkBitmapFilter(width) {}
float evaluate(float x) const override {
if (x <= -fWidth || x >= fWidth) {
return 0.0f; // Outside of the window.
}
if (x > -FLT_EPSILON && x < FLT_EPSILON) {
return 1.0f; // Special case the discontinuity at the origin.
}
float xpi = x * float(M_PI);
return (sinf(xpi) / xpi) * // sinc(x)
sinf(xpi / fWidth) / (xpi / fWidth); // sinc(x/fWidth)
}
};
// Represents a filter in one dimension. Each output pixel has one entry in this
// object for the filter values contributing to it. You build up the filter
// list by calling AddFilter for each output pixel (in order).
//
// We do 2-dimensional convolution by first convolving each row by one
// SkConvolutionFilter1D, then convolving each column by another one.
//
// Entries are stored in ConvolutionFixed point, shifted left by kShiftBits.
class SkConvolutionFilter1D {
public:
using ConvolutionFixed = short;
// The number of bits that ConvolutionFixed point values are shifted by.
enum { kShiftBits = 14 };
SkConvolutionFilter1D();
~SkConvolutionFilter1D();
// Convert between floating point and our ConvolutionFixed point
// representation.
static ConvolutionFixed ToFixed(float f) {
return static_cast<ConvolutionFixed>(f * (1 << kShiftBits));
}
// Returns the maximum pixel span of a filter.
int maxFilter() const { return fMaxFilter; }
// Returns the number of filters in this filter. This is the dimension of the
// output image.
int numValues() const { return static_cast<int>(fFilters.length()); }
bool reserveAdditional(int filterCount, int filterValueCount) {
return fFilters.reserve(fFilters.length() + filterCount) &&
fFilterValues.reserve(fFilterValues.length() + filterValueCount);
}
// Appends the given list of scaling values for generating a given output
// pixel. |filterOffset| is the distance from the edge of the image to where
// the scaling factors start. The scaling factors apply to the source pixels
// starting from this position, and going for the next |filterLength| pixels.
//
// You will probably want to make sure your input is normalized (that is,
// all entries in |filterValuesg| sub to one) to prevent affecting the overall
// brighness of the image.
//
// The filterLength must be > 0.
bool AddFilter(int filterOffset, const ConvolutionFixed* filterValues,
int filterLength);
// Retrieves a filter for the given |valueOffset|, a position in the output
// image in the direction we're convolving. The offset and length of the
// filter values are put into the corresponding out arguments (see AddFilter
// above for what these mean), and a pointer to the first scaling factor is
// returned. There will be |filterLength| values in this array.
inline const ConvolutionFixed* FilterForValue(int valueOffset,
int* filterOffset,
int* filterLength) const {
const FilterInstance& filter = fFilters[valueOffset];
*filterOffset = filter.fOffset;
*filterLength = filter.fTrimmedLength;
if (filter.fTrimmedLength == 0) {
return nullptr;
}
return &fFilterValues[filter.fDataLocation];
}
bool ComputeFilterValues(const SkBitmapFilter& aBitmapFilter,
int32_t aSrcSize, int32_t aDstSize);
private:
struct FilterInstance {
// Offset within filterValues for this instance of the filter.
int fDataLocation;
// Distance from the left of the filter to the center. IN PIXELS
int fOffset;
// Number of values in this filter instance.
int fTrimmedLength;
// Filter length as specified. Note that this may be different from
// 'trimmed_length' if leading/trailing zeros of the original floating
// point form were clipped differently on each tail.
int fLength;
};
// Stores the information for each filter added to this class.
mozilla::Vector<FilterInstance> fFilters;
// We store all the filter values in this flat list, indexed by
// |FilterInstance.data_location| to avoid the mallocs required for storing
// each one separately.
mozilla::Vector<ConvolutionFixed> fFilterValues;
// The maximum size of any filter we've added.
int fMaxFilter;
};
void convolve_horizontally(const unsigned char* srcData,
const SkConvolutionFilter1D& filter,
unsigned char* outRow, bool hasAlpha);
void convolve_vertically(
const SkConvolutionFilter1D::ConvolutionFixed* filterValues,
int filterLength, unsigned char* const* sourceDataRows, int pixelWidth,
unsigned char* outRow, bool hasAlpha);
bool BGRAConvolve2D(const unsigned char* sourceData, int sourceByteRowStride,
bool sourceHasAlpha, const SkConvolutionFilter1D& filterX,
const SkConvolutionFilter1D& filterY,
int outputByteRowStride, unsigned char* output);
} // namespace skia
#endif /* MOZILLA_GFX_SKCONVOLVER_H_ */