LCOV - code coverage report
Current view: top level - gfx/skia/skia/src/core - SkBitmapScaler.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 93 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 8 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright 2015 Google Inc.
       3             :  *
       4             :  * Use of this source code is governed by a BSD-style license that can be
       5             :  * found in the LICENSE file.
       6             :  */
       7             : 
       8             : #include "SkBitmapScaler.h"
       9             : #include "SkBitmapFilter.h"
      10             : #include "SkConvolver.h"
      11             : #include "SkImageInfo.h"
      12             : #include "SkPixmap.h"
      13             : #include "SkRect.h"
      14             : #include "SkTArray.h"
      15             : 
      16             : // SkResizeFilter ----------------------------------------------------------------
      17             : 
      18             : // Encapsulates computation and storage of the filters required for one complete
      19             : // resize operation.
      20             : class SkResizeFilter {
      21             : public:
      22             :     SkResizeFilter(SkBitmapScaler::ResizeMethod method,
      23             :                    int srcFullWidth, int srcFullHeight,
      24             :                    float destWidth, float destHeight,
      25             :                    const SkRect& destSubset);
      26           0 :     ~SkResizeFilter() { delete fBitmapFilter; }
      27             : 
      28             :     // Returns the filled filter values.
      29           0 :     const SkConvolutionFilter1D& xFilter() { return fXFilter; }
      30           0 :     const SkConvolutionFilter1D& yFilter() { return fYFilter; }
      31             : 
      32             : private:
      33             : 
      34             :     SkBitmapFilter* fBitmapFilter;
      35             : 
      36             :     // Computes one set of filters either horizontally or vertically. The caller
      37             :     // will specify the "min" and "max" rather than the bottom/top and
      38             :     // right/bottom so that the same code can be re-used in each dimension.
      39             :     //
      40             :     // |srcDependLo| and |srcDependSize| gives the range for the source
      41             :     // depend rectangle (horizontally or vertically at the caller's discretion
      42             :     // -- see above for what this means).
      43             :     //
      44             :     // Likewise, the range of destination values to compute and the scale factor
      45             :     // for the transform is also specified.
      46             : 
      47             :     void computeFilters(int srcSize,
      48             :                         float destSubsetLo, float destSubsetSize,
      49             :                         float scale,
      50             :                         SkConvolutionFilter1D* output);
      51             : 
      52             :     SkConvolutionFilter1D fXFilter;
      53             :     SkConvolutionFilter1D fYFilter;
      54             : };
      55             : 
      56           0 : SkResizeFilter::SkResizeFilter(SkBitmapScaler::ResizeMethod method,
      57             :                                int srcFullWidth, int srcFullHeight,
      58             :                                float destWidth, float destHeight,
      59           0 :                                const SkRect& destSubset) {
      60             : 
      61           0 :     SkASSERT(method >= SkBitmapScaler::RESIZE_FirstMethod &&
      62             :              method <= SkBitmapScaler::RESIZE_LastMethod);
      63             : 
      64           0 :     fBitmapFilter = nullptr;
      65           0 :     switch(method) {
      66             :         case SkBitmapScaler::RESIZE_BOX:
      67           0 :             fBitmapFilter = new SkBoxFilter;
      68           0 :             break;
      69             :         case SkBitmapScaler::RESIZE_TRIANGLE:
      70           0 :             fBitmapFilter = new SkTriangleFilter;
      71           0 :             break;
      72             :         case SkBitmapScaler::RESIZE_MITCHELL:
      73           0 :             fBitmapFilter = new SkMitchellFilter;
      74           0 :             break;
      75             :         case SkBitmapScaler::RESIZE_HAMMING:
      76           0 :             fBitmapFilter = new SkHammingFilter;
      77           0 :             break;
      78             :         case SkBitmapScaler::RESIZE_LANCZOS3:
      79           0 :             fBitmapFilter = new SkLanczosFilter;
      80           0 :             break;
      81             :     }
      82             : 
      83             : 
      84           0 :     float scaleX = destWidth / srcFullWidth;
      85           0 :     float scaleY = destHeight / srcFullHeight;
      86             : 
      87           0 :     this->computeFilters(srcFullWidth, destSubset.fLeft, destSubset.width(),
      88           0 :                          scaleX, &fXFilter);
      89           0 :     if (srcFullWidth == srcFullHeight &&
      90           0 :         destSubset.fLeft == destSubset.fTop &&
      91           0 :         destSubset.width() == destSubset.height()&&
      92             :         scaleX == scaleY) {
      93           0 :         fYFilter = fXFilter;
      94             :     } else {
      95           0 :         this->computeFilters(srcFullHeight, destSubset.fTop, destSubset.height(),
      96           0 :                           scaleY, &fYFilter);
      97             :     }
      98           0 : }
      99             : 
     100             : // TODO(egouriou): Take advantage of periods in the convolution.
     101             : // Practical resizing filters are periodic outside of the border area.
     102             : // For Lanczos, a scaling by a (reduced) factor of p/q (q pixels in the
     103             : // source become p pixels in the destination) will have a period of p.
     104             : // A nice consequence is a period of 1 when downscaling by an integral
     105             : // factor. Downscaling from typical display resolutions is also bound
     106             : // to produce interesting periods as those are chosen to have multiple
     107             : // small factors.
     108             : // Small periods reduce computational load and improve cache usage if
     109             : // the coefficients can be shared. For periods of 1 we can consider
     110             : // loading the factors only once outside the borders.
     111           0 : void SkResizeFilter::computeFilters(int srcSize,
     112             :                                     float destSubsetLo, float destSubsetSize,
     113             :                                     float scale,
     114             :                                     SkConvolutionFilter1D* output) {
     115           0 :     float destSubsetHi = destSubsetLo + destSubsetSize;  // [lo, hi)
     116             : 
     117             :     // When we're doing a magnification, the scale will be larger than one. This
     118             :     // means the destination pixels are much smaller than the source pixels, and
     119             :     // that the range covered by the filter won't necessarily cover any source
     120             :     // pixel boundaries. Therefore, we use these clamped values (max of 1) for
     121             :     // some computations.
     122           0 :     float clampedScale = SkTMin(1.0f, scale);
     123             : 
     124             :     // This is how many source pixels from the center we need to count
     125             :     // to support the filtering function.
     126           0 :     float srcSupport = fBitmapFilter->width() / clampedScale;
     127             : 
     128           0 :     float invScale = 1.0f / scale;
     129             : 
     130           0 :     SkSTArray<64, float, true> filterValuesArray;
     131           0 :     SkSTArray<64, SkConvolutionFilter1D::ConvolutionFixed, true> fixedFilterValuesArray;
     132             : 
     133             :     // Loop over all pixels in the output range. We will generate one set of
     134             :     // filter values for each one. Those values will tell us how to blend the
     135             :     // source pixels to compute the destination pixel.
     136             : 
     137             :     // This is the pixel in the source directly under the pixel in the dest.
     138             :     // Note that we base computations on the "center" of the pixels. To see
     139             :     // why, observe that the destination pixel at coordinates (0, 0) in a 5.0x
     140             :     // downscale should "cover" the pixels around the pixel with *its center*
     141             :     // at coordinates (2.5, 2.5) in the source, not those around (0, 0).
     142             :     // Hence we need to scale coordinates (0.5, 0.5), not (0, 0).
     143           0 :     destSubsetLo = SkScalarFloorToScalar(destSubsetLo);
     144           0 :     destSubsetHi = SkScalarCeilToScalar(destSubsetHi);
     145           0 :     float srcPixel = (destSubsetLo + 0.5f) * invScale;
     146           0 :     int destLimit = SkScalarTruncToInt(destSubsetHi - destSubsetLo);
     147           0 :     output->reserveAdditional(destLimit, SkScalarCeilToInt(destLimit * srcSupport * 2));
     148           0 :     for (int destI = 0; destI < destLimit; srcPixel += invScale, destI++) {
     149             :         // Compute the (inclusive) range of source pixels the filter covers.
     150           0 :         float srcBegin = SkTMax(0.f, SkScalarFloorToScalar(srcPixel - srcSupport));
     151           0 :         float srcEnd = SkTMin(srcSize - 1.f, SkScalarCeilToScalar(srcPixel + srcSupport));
     152             : 
     153             :         // Compute the unnormalized filter value at each location of the source
     154             :         // it covers.
     155             : 
     156             :         // Sum of the filter values for normalizing.
     157             :         // Distance from the center of the filter, this is the filter coordinate
     158             :         // in source space. We also need to consider the center of the pixel
     159             :         // when comparing distance against 'srcPixel'. In the 5x downscale
     160             :         // example used above the distance from the center of the filter to
     161             :         // the pixel with coordinates (2, 2) should be 0, because its center
     162             :         // is at (2.5, 2.5).
     163           0 :         float destFilterDist = (srcBegin + 0.5f - srcPixel) * clampedScale;
     164           0 :         int filterCount = SkScalarTruncToInt(srcEnd - srcBegin) + 1;
     165           0 :         if (filterCount <= 0) {
     166             :             // true when srcSize is equal to srcPixel - srcSupport; this may be a bug
     167           0 :             return;
     168             :         }
     169           0 :         filterValuesArray.reset(filterCount);
     170           0 :         float filterSum = fBitmapFilter->evaluate_n(destFilterDist, clampedScale, filterCount,
     171           0 :                                                 filterValuesArray.begin());
     172             : 
     173             :         // The filter must be normalized so that we don't affect the brightness of
     174             :         // the image. Convert to normalized fixed point.
     175           0 :         int fixedSum = 0;
     176           0 :         fixedFilterValuesArray.reset(filterCount);
     177           0 :         const float* filterValues = filterValuesArray.begin();
     178           0 :         SkConvolutionFilter1D::ConvolutionFixed* fixedFilterValues = fixedFilterValuesArray.begin();
     179           0 :         float invFilterSum = 1 / filterSum;
     180           0 :         for (int fixedI = 0; fixedI < filterCount; fixedI++) {
     181           0 :             int curFixed = SkConvolutionFilter1D::FloatToFixed(filterValues[fixedI] * invFilterSum);
     182           0 :             fixedSum += curFixed;
     183           0 :             fixedFilterValues[fixedI] = SkToS16(curFixed);
     184             :         }
     185           0 :         SkASSERT(fixedSum <= 0x7FFF);
     186             : 
     187             :         // The conversion to fixed point will leave some rounding errors, which
     188             :         // we add back in to avoid affecting the brightness of the image. We
     189             :         // arbitrarily add this to the center of the filter array (this won't always
     190             :         // be the center of the filter function since it could get clipped on the
     191             :         // edges, but it doesn't matter enough to worry about that case).
     192           0 :         int leftovers = SkConvolutionFilter1D::FloatToFixed(1) - fixedSum;
     193           0 :         fixedFilterValues[filterCount / 2] += leftovers;
     194             : 
     195             :         // Now it's ready to go.
     196           0 :         output->AddFilter(SkScalarFloorToInt(srcBegin), fixedFilterValues, filterCount);
     197             :     }
     198             : }
     199             : 
     200             : ///////////////////////////////////////////////////////////////////////////////////////////////////
     201             : 
     202           0 : static bool valid_for_resize(const SkPixmap& source, int dstW, int dstH) {
     203             :     // TODO: Seems like we shouldn't care about the swizzle of source, just that it's 8888
     204           0 :     return source.addr() && source.colorType() == kN32_SkColorType &&
     205           0 :            source.width() >= 1 && source.height() >= 1 && dstW >= 1 && dstH >= 1;
     206             : }
     207             : 
     208           0 : bool SkBitmapScaler::Resize(const SkPixmap& result, const SkPixmap& source, ResizeMethod method) {
     209           0 :     if (!valid_for_resize(source, result.width(), result.height())) {
     210           0 :         return false;
     211             :     }
     212           0 :     if (!result.addr() || result.colorType() != source.colorType()) {
     213           0 :         return false;
     214             :     }
     215             : 
     216           0 :     SkRect destSubset = SkRect::MakeIWH(result.width(), result.height());
     217             : 
     218             :     SkResizeFilter filter(method, source.width(), source.height(),
     219           0 :                           result.width(), result.height(), destSubset);
     220             : 
     221             :     // Get a subset encompassing this touched area. We construct the
     222             :     // offsets and row strides such that it looks like a new bitmap, while
     223             :     // referring to the old data.
     224           0 :     const uint8_t* sourceSubset = reinterpret_cast<const uint8_t*>(source.addr());
     225             : 
     226           0 :     return BGRAConvolve2D(sourceSubset, static_cast<int>(source.rowBytes()),
     227           0 :                           !source.isOpaque(), filter.xFilter(), filter.yFilter(),
     228           0 :                           static_cast<int>(result.rowBytes()),
     229           0 :                           static_cast<unsigned char*>(result.writable_addr()));
     230             : }
     231             : 
     232           0 : bool SkBitmapScaler::Resize(SkBitmap* resultPtr, const SkPixmap& source, ResizeMethod method,
     233             :                             int destWidth, int destHeight, SkBitmap::Allocator* allocator) {
     234             :     // Preflight some of the checks, to avoid allocating the result if we don't need it.
     235           0 :     if (!valid_for_resize(source, destWidth, destHeight)) {
     236           0 :         return false;
     237             :     }
     238             : 
     239           0 :     SkBitmap result;
     240             :     // Note: pass along the profile information even thought this is no the right answer because
     241             :     // this could be scaling in sRGB.
     242           0 :     result.setInfo(SkImageInfo::MakeN32(destWidth, destHeight, source.alphaType(),
     243           0 :                                         sk_ref_sp(source.info().colorSpace())));
     244           0 :     result.allocPixels(allocator, nullptr);
     245             : 
     246           0 :     SkPixmap resultPM;
     247           0 :     if (!result.peekPixels(&resultPM) || !Resize(resultPM, source, method)) {
     248           0 :         return false;
     249             :     }
     250             : 
     251           0 :     *resultPtr = result;
     252           0 :     resultPtr->lockPixels();
     253           0 :     SkASSERT(resultPtr->getPixels());
     254           0 :     return true;
     255             : }

Generated by: LCOV version 1.13