gfx/skia/trunk/src/core/SkBitmapScaler.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 #include "SkBitmapScaler.h"
     2 #include "SkBitmapFilter.h"
     3 #include "SkRect.h"
     4 #include "SkTArray.h"
     5 #include "SkErrorInternals.h"
     6 #include "SkConvolver.h"
     8 // SkResizeFilter ----------------------------------------------------------------
    10 // Encapsulates computation and storage of the filters required for one complete
    11 // resize operation.
    12 class SkResizeFilter {
    13 public:
    14     SkResizeFilter(SkBitmapScaler::ResizeMethod method,
    15                    int srcFullWidth, int srcFullHeight,
    16                    int destWidth, int destHeight,
    17                    const SkIRect& destSubset,
    18                    const SkConvolutionProcs& convolveProcs);
    19     ~SkResizeFilter() {
    20         SkDELETE( fBitmapFilter );
    21     }
    23     // Returns the filled filter values.
    24     const SkConvolutionFilter1D& xFilter() { return fXFilter; }
    25     const SkConvolutionFilter1D& yFilter() { return fYFilter; }
    27 private:
    29     SkBitmapFilter* fBitmapFilter;
    31     // Computes one set of filters either horizontally or vertically. The caller
    32     // will specify the "min" and "max" rather than the bottom/top and
    33     // right/bottom so that the same code can be re-used in each dimension.
    34     //
    35     // |srcDependLo| and |srcDependSize| gives the range for the source
    36     // depend rectangle (horizontally or vertically at the caller's discretion
    37     // -- see above for what this means).
    38     //
    39     // Likewise, the range of destination values to compute and the scale factor
    40     // for the transform is also specified.
    42     void computeFilters(int srcSize,
    43                         int destSubsetLo, int destSubsetSize,
    44                         float scale,
    45                         SkConvolutionFilter1D* output,
    46                         const SkConvolutionProcs& convolveProcs);
    48     SkConvolutionFilter1D fXFilter;
    49     SkConvolutionFilter1D fYFilter;
    50 };
    52 SkResizeFilter::SkResizeFilter(SkBitmapScaler::ResizeMethod method,
    53                                int srcFullWidth, int srcFullHeight,
    54                                int destWidth, int destHeight,
    55                                const SkIRect& destSubset,
    56                                const SkConvolutionProcs& convolveProcs) {
    58     // method will only ever refer to an "algorithm method".
    59     SkASSERT((SkBitmapScaler::RESIZE_FIRST_ALGORITHM_METHOD <= method) &&
    60              (method <= SkBitmapScaler::RESIZE_LAST_ALGORITHM_METHOD));
    62     switch(method) {
    63         case SkBitmapScaler::RESIZE_BOX:
    64             fBitmapFilter = SkNEW(SkBoxFilter);
    65             break;
    66         case SkBitmapScaler::RESIZE_TRIANGLE:
    67             fBitmapFilter = SkNEW(SkTriangleFilter);
    68             break;
    69         case SkBitmapScaler::RESIZE_MITCHELL:
    70             fBitmapFilter = SkNEW_ARGS(SkMitchellFilter, (1.f/3.f, 1.f/3.f));
    71             break;
    72         case SkBitmapScaler::RESIZE_HAMMING:
    73             fBitmapFilter = SkNEW(SkHammingFilter);
    74             break;
    75         case SkBitmapScaler::RESIZE_LANCZOS3:
    76             fBitmapFilter = SkNEW(SkLanczosFilter);
    77             break;
    78         default:
    79             // NOTREACHED:
    80             fBitmapFilter = SkNEW_ARGS(SkMitchellFilter, (1.f/3.f, 1.f/3.f));
    81             break;
    82     }
    85     float scaleX = static_cast<float>(destWidth) /
    86                    static_cast<float>(srcFullWidth);
    87     float scaleY = static_cast<float>(destHeight) /
    88                    static_cast<float>(srcFullHeight);
    90     this->computeFilters(srcFullWidth, destSubset.fLeft, destSubset.width(),
    91                          scaleX, &fXFilter, convolveProcs);
    92     if (srcFullWidth == srcFullHeight &&
    93         destSubset.fLeft == destSubset.fTop &&
    94         destSubset.width() == destSubset.height()&&
    95         scaleX == scaleY) {
    96         fYFilter = fXFilter;
    97     } else {
    98         this->computeFilters(srcFullHeight, destSubset.fTop, destSubset.height(),
    99                           scaleY, &fYFilter, convolveProcs);
   100     }
   101 }
   103 // TODO(egouriou): Take advantage of periods in the convolution.
   104 // Practical resizing filters are periodic outside of the border area.
   105 // For Lanczos, a scaling by a (reduced) factor of p/q (q pixels in the
   106 // source become p pixels in the destination) will have a period of p.
   107 // A nice consequence is a period of 1 when downscaling by an integral
   108 // factor. Downscaling from typical display resolutions is also bound
   109 // to produce interesting periods as those are chosen to have multiple
   110 // small factors.
   111 // Small periods reduce computational load and improve cache usage if
   112 // the coefficients can be shared. For periods of 1 we can consider
   113 // loading the factors only once outside the borders.
   114 void SkResizeFilter::computeFilters(int srcSize,
   115                                   int destSubsetLo, int destSubsetSize,
   116                                   float scale,
   117                                   SkConvolutionFilter1D* output,
   118                                   const SkConvolutionProcs& convolveProcs) {
   119   int destSubsetHi = destSubsetLo + destSubsetSize;  // [lo, hi)
   121   // When we're doing a magnification, the scale will be larger than one. This
   122   // means the destination pixels are much smaller than the source pixels, and
   123   // that the range covered by the filter won't necessarily cover any source
   124   // pixel boundaries. Therefore, we use these clamped values (max of 1) for
   125   // some computations.
   126   float clampedScale = SkTMin(1.0f, scale);
   128   // This is how many source pixels from the center we need to count
   129   // to support the filtering function.
   130   float srcSupport = fBitmapFilter->width() / clampedScale;
   132   // Speed up the divisions below by turning them into multiplies.
   133   float invScale = 1.0f / scale;
   135   SkTArray<float> filterValues(64);
   136   SkTArray<short> fixedFilterValues(64);
   138   // Loop over all pixels in the output range. We will generate one set of
   139   // filter values for each one. Those values will tell us how to blend the
   140   // source pixels to compute the destination pixel.
   141   for (int destSubsetI = destSubsetLo; destSubsetI < destSubsetHi;
   142        destSubsetI++) {
   143     // Reset the arrays. We don't declare them inside so they can re-use the
   144     // same malloc-ed buffer.
   145     filterValues.reset();
   146     fixedFilterValues.reset();
   148     // This is the pixel in the source directly under the pixel in the dest.
   149     // Note that we base computations on the "center" of the pixels. To see
   150     // why, observe that the destination pixel at coordinates (0, 0) in a 5.0x
   151     // downscale should "cover" the pixels around the pixel with *its center*
   152     // at coordinates (2.5, 2.5) in the source, not those around (0, 0).
   153     // Hence we need to scale coordinates (0.5, 0.5), not (0, 0).
   154     float srcPixel = (static_cast<float>(destSubsetI) + 0.5f) * invScale;
   156     // Compute the (inclusive) range of source pixels the filter covers.
   157     int srcBegin = SkTMax(0, SkScalarFloorToInt(srcPixel - srcSupport));
   158     int srcEnd = SkTMin(srcSize - 1, SkScalarCeilToInt(srcPixel + srcSupport));
   160     // Compute the unnormalized filter value at each location of the source
   161     // it covers.
   162     float filterSum = 0.0f;  // Sub of the filter values for normalizing.
   163     for (int curFilterPixel = srcBegin; curFilterPixel <= srcEnd;
   164          curFilterPixel++) {
   165       // Distance from the center of the filter, this is the filter coordinate
   166       // in source space. We also need to consider the center of the pixel
   167       // when comparing distance against 'srcPixel'. In the 5x downscale
   168       // example used above the distance from the center of the filter to
   169       // the pixel with coordinates (2, 2) should be 0, because its center
   170       // is at (2.5, 2.5).
   171       float srcFilterDist =
   172           ((static_cast<float>(curFilterPixel) + 0.5f) - srcPixel);
   174       // Since the filter really exists in dest space, map it there.
   175       float destFilterDist = srcFilterDist * clampedScale;
   177       // Compute the filter value at that location.
   178       float filterValue = fBitmapFilter->evaluate(destFilterDist);
   179       filterValues.push_back(filterValue);
   181       filterSum += filterValue;
   182     }
   183     SkASSERT(!filterValues.empty());
   185     // The filter must be normalized so that we don't affect the brightness of
   186     // the image. Convert to normalized fixed point.
   187     short fixedSum = 0;
   188     for (int i = 0; i < filterValues.count(); i++) {
   189       short curFixed = output->FloatToFixed(filterValues[i] / filterSum);
   190       fixedSum += curFixed;
   191       fixedFilterValues.push_back(curFixed);
   192     }
   194     // The conversion to fixed point will leave some rounding errors, which
   195     // we add back in to avoid affecting the brightness of the image. We
   196     // arbitrarily add this to the center of the filter array (this won't always
   197     // be the center of the filter function since it could get clipped on the
   198     // edges, but it doesn't matter enough to worry about that case).
   199     short leftovers = output->FloatToFixed(1.0f) - fixedSum;
   200     fixedFilterValues[fixedFilterValues.count() / 2] += leftovers;
   202     // Now it's ready to go.
   203     output->AddFilter(srcBegin, &fixedFilterValues[0],
   204                       static_cast<int>(fixedFilterValues.count()));
   205   }
   207   if (convolveProcs.fApplySIMDPadding) {
   208       convolveProcs.fApplySIMDPadding( output );
   209   }
   210 }
   212 static SkBitmapScaler::ResizeMethod ResizeMethodToAlgorithmMethod(
   213                                     SkBitmapScaler::ResizeMethod method) {
   214     // Convert any "Quality Method" into an "Algorithm Method"
   215     if (method >= SkBitmapScaler::RESIZE_FIRST_ALGORITHM_METHOD &&
   216     method <= SkBitmapScaler::RESIZE_LAST_ALGORITHM_METHOD) {
   217         return method;
   218     }
   219     // The call to SkBitmapScalerGtv::Resize() above took care of
   220     // GPU-acceleration in the cases where it is possible. So now we just
   221     // pick the appropriate software method for each resize quality.
   222     switch (method) {
   223         // Users of RESIZE_GOOD are willing to trade a lot of quality to
   224         // get speed, allowing the use of linear resampling to get hardware
   225         // acceleration (SRB). Hence any of our "good" software filters
   226         // will be acceptable, so we use a triangle.
   227         case SkBitmapScaler::RESIZE_GOOD:
   228             return SkBitmapScaler::RESIZE_TRIANGLE;
   229         // Users of RESIZE_BETTER are willing to trade some quality in order
   230         // to improve performance, but are guaranteed not to devolve to a linear
   231         // resampling. In visual tests we see that Hamming-1 is not as good as
   232         // Lanczos-2, however it is about 40% faster and Lanczos-2 itself is
   233         // about 30% faster than Lanczos-3. The use of Hamming-1 has been deemed
   234         // an acceptable trade-off between quality and speed.
   235         case SkBitmapScaler::RESIZE_BETTER:
   236             return SkBitmapScaler::RESIZE_HAMMING;
   237         default:
   238 #ifdef SK_HIGH_QUALITY_IS_LANCZOS
   239             return SkBitmapScaler::RESIZE_LANCZOS3;
   240 #else
   241             return SkBitmapScaler::RESIZE_MITCHELL;
   242 #endif
   243     }
   244 }
   246 // static
   247 bool SkBitmapScaler::Resize(SkBitmap* resultPtr,
   248                             const SkBitmap& source,
   249                             ResizeMethod method,
   250                             int destWidth, int destHeight,
   251                             const SkIRect& destSubset,
   252                             const SkConvolutionProcs& convolveProcs,
   253                             SkBitmap::Allocator* allocator) {
   254   // Ensure that the ResizeMethod enumeration is sound.
   255     SkASSERT(((RESIZE_FIRST_QUALITY_METHOD <= method) &&
   256         (method <= RESIZE_LAST_QUALITY_METHOD)) ||
   257         ((RESIZE_FIRST_ALGORITHM_METHOD <= method) &&
   258         (method <= RESIZE_LAST_ALGORITHM_METHOD)));
   260     SkIRect dest = { 0, 0, destWidth, destHeight };
   261     if (!dest.contains(destSubset)) {
   262         SkErrorInternals::SetError( kInvalidArgument_SkError,
   263                                     "Sorry, you passed me a bitmap resize "
   264                                     " method I have never heard of: %d",
   265                                     method );
   266     }
   268     // If the size of source or destination is 0, i.e. 0x0, 0xN or Nx0, just
   269     // return empty.
   270     if (source.width() < 1 || source.height() < 1 ||
   271         destWidth < 1 || destHeight < 1) {
   272         // todo: seems like we could handle negative dstWidth/Height, since that
   273         // is just a negative scale (flip)
   274         return false;
   275     }
   277     method = ResizeMethodToAlgorithmMethod(method);
   279     // Check that we deal with an "algorithm methods" from this point onward.
   280     SkASSERT((SkBitmapScaler::RESIZE_FIRST_ALGORITHM_METHOD <= method) &&
   281         (method <= SkBitmapScaler::RESIZE_LAST_ALGORITHM_METHOD));
   283     SkAutoLockPixels locker(source);
   284     if (!source.readyToDraw() ||
   285         source.colorType() != kPMColor_SkColorType) {
   286         return false;
   287     }
   289     SkResizeFilter filter(method, source.width(), source.height(),
   290                           destWidth, destHeight, destSubset, convolveProcs);
   292     // Get a source bitmap encompassing this touched area. We construct the
   293     // offsets and row strides such that it looks like a new bitmap, while
   294     // referring to the old data.
   295     const unsigned char* sourceSubset =
   296         reinterpret_cast<const unsigned char*>(source.getPixels());
   298     // Convolve into the result.
   299     SkBitmap result;
   300     result.setConfig(SkImageInfo::MakeN32(destSubset.width(),
   301                                           destSubset.height(),
   302                                           source.alphaType()));
   303     result.allocPixels(allocator, NULL);
   304     if (!result.readyToDraw()) {
   305         return false;
   306     }
   308     BGRAConvolve2D(sourceSubset, static_cast<int>(source.rowBytes()),
   309         !source.isOpaque(), filter.xFilter(), filter.yFilter(),
   310         static_cast<int>(result.rowBytes()),
   311         static_cast<unsigned char*>(result.getPixels()),
   312         convolveProcs, true);
   314     *resultPtr = result;
   315     resultPtr->lockPixels();
   316     SkASSERT(NULL != resultPtr->getPixels());
   317     return true;
   318 }
   320 // static
   321 bool SkBitmapScaler::Resize(SkBitmap* resultPtr,
   322                             const SkBitmap& source,
   323                             ResizeMethod method,
   324                             int destWidth, int destHeight,
   325                             const SkConvolutionProcs& convolveProcs,
   326                             SkBitmap::Allocator* allocator) {
   327     SkIRect destSubset = { 0, 0, destWidth, destHeight };
   328     return Resize(resultPtr, source, method, destWidth, destHeight, destSubset,
   329                   convolveProcs, allocator);
   330 }

mercurial