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.

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

mercurial