1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/2d/image_operations.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,552 @@ 1.4 +// Copyright (c) 2006-2012 The Chromium Authors. All rights reserved. 1.5 +// 1.6 +// Redistribution and use in source and binary forms, with or without 1.7 +// modification, are permitted provided that the following conditions 1.8 +// are met: 1.9 +// * Redistributions of source code must retain the above copyright 1.10 +// notice, this list of conditions and the following disclaimer. 1.11 +// * Redistributions in binary form must reproduce the above copyright 1.12 +// notice, this list of conditions and the following disclaimer in 1.13 +// the documentation and/or other materials provided with the 1.14 +// distribution. 1.15 +// * Neither the name of Google, Inc. nor the names of its contributors 1.16 +// may be used to endorse or promote products derived from this 1.17 +// software without specific prior written permission. 1.18 +// 1.19 +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1.20 +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1.21 +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 1.22 +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 1.23 +// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 1.24 +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 1.25 +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 1.26 +// OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 1.27 +// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 1.28 +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 1.29 +// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 1.30 +// SUCH DAMAGE. 1.31 + 1.32 +#include "base/basictypes.h" 1.33 + 1.34 +#define _USE_MATH_DEFINES 1.35 +#include <algorithm> 1.36 +#include <cmath> 1.37 +#include <limits> 1.38 + 1.39 +#include "image_operations.h" 1.40 + 1.41 +#include "base/stack_container.h" 1.42 +#include "convolver.h" 1.43 +#include "skia/SkColorPriv.h" 1.44 +#include "skia/SkBitmap.h" 1.45 +#include "skia/SkRect.h" 1.46 +#include "skia/SkFontHost.h" 1.47 + 1.48 +namespace skia { 1.49 + 1.50 +namespace { 1.51 + 1.52 +// Returns the ceiling/floor as an integer. 1.53 +inline int CeilInt(float val) { 1.54 + return static_cast<int>(ceil(val)); 1.55 +} 1.56 +inline int FloorInt(float val) { 1.57 + return static_cast<int>(floor(val)); 1.58 +} 1.59 + 1.60 +// Filter function computation ------------------------------------------------- 1.61 + 1.62 +// Evaluates the box filter, which goes from -0.5 to +0.5. 1.63 +float EvalBox(float x) { 1.64 + return (x >= -0.5f && x < 0.5f) ? 1.0f : 0.0f; 1.65 +} 1.66 + 1.67 +// Evaluates the Lanczos filter of the given filter size window for the given 1.68 +// position. 1.69 +// 1.70 +// |filter_size| is the width of the filter (the "window"), outside of which 1.71 +// the value of the function is 0. Inside of the window, the value is the 1.72 +// normalized sinc function: 1.73 +// lanczos(x) = sinc(x) * sinc(x / filter_size); 1.74 +// where 1.75 +// sinc(x) = sin(pi*x) / (pi*x); 1.76 +float EvalLanczos(int filter_size, float x) { 1.77 + if (x <= -filter_size || x >= filter_size) 1.78 + return 0.0f; // Outside of the window. 1.79 + if (x > -std::numeric_limits<float>::epsilon() && 1.80 + x < std::numeric_limits<float>::epsilon()) 1.81 + return 1.0f; // Special case the discontinuity at the origin. 1.82 + float xpi = x * static_cast<float>(M_PI); 1.83 + return (sin(xpi) / xpi) * // sinc(x) 1.84 + sin(xpi / filter_size) / (xpi / filter_size); // sinc(x/filter_size) 1.85 +} 1.86 + 1.87 +// Evaluates the Hamming filter of the given filter size window for the given 1.88 +// position. 1.89 +// 1.90 +// The filter covers [-filter_size, +filter_size]. Outside of this window 1.91 +// the value of the function is 0. Inside of the window, the value is sinus 1.92 +// cardinal multiplied by a recentered Hamming function. The traditional 1.93 +// Hamming formula for a window of size N and n ranging in [0, N-1] is: 1.94 +// hamming(n) = 0.54 - 0.46 * cos(2 * pi * n / (N-1))) 1.95 +// In our case we want the function centered for x == 0 and at its minimum 1.96 +// on both ends of the window (x == +/- filter_size), hence the adjusted 1.97 +// formula: 1.98 +// hamming(x) = (0.54 - 1.99 +// 0.46 * cos(2 * pi * (x - filter_size)/ (2 * filter_size))) 1.100 +// = 0.54 - 0.46 * cos(pi * x / filter_size - pi) 1.101 +// = 0.54 + 0.46 * cos(pi * x / filter_size) 1.102 +float EvalHamming(int filter_size, float x) { 1.103 + if (x <= -filter_size || x >= filter_size) 1.104 + return 0.0f; // Outside of the window. 1.105 + if (x > -std::numeric_limits<float>::epsilon() && 1.106 + x < std::numeric_limits<float>::epsilon()) 1.107 + return 1.0f; // Special case the sinc discontinuity at the origin. 1.108 + const float xpi = x * static_cast<float>(M_PI); 1.109 + 1.110 + return ((sin(xpi) / xpi) * // sinc(x) 1.111 + (0.54f + 0.46f * cos(xpi / filter_size))); // hamming(x) 1.112 +} 1.113 + 1.114 +// ResizeFilter ---------------------------------------------------------------- 1.115 + 1.116 +// Encapsulates computation and storage of the filters required for one complete 1.117 +// resize operation. 1.118 +class ResizeFilter { 1.119 + public: 1.120 + ResizeFilter(ImageOperations::ResizeMethod method, 1.121 + int src_full_width, int src_full_height, 1.122 + int dest_width, int dest_height, 1.123 + const SkIRect& dest_subset); 1.124 + 1.125 + // Returns the filled filter values. 1.126 + const ConvolutionFilter1D& x_filter() { return x_filter_; } 1.127 + const ConvolutionFilter1D& y_filter() { return y_filter_; } 1.128 + 1.129 + private: 1.130 + // Returns the number of pixels that the filer spans, in filter space (the 1.131 + // destination image). 1.132 + float GetFilterSupport(float scale) { 1.133 + switch (method_) { 1.134 + case ImageOperations::RESIZE_BOX: 1.135 + // The box filter just scales with the image scaling. 1.136 + return 0.5f; // Only want one side of the filter = /2. 1.137 + case ImageOperations::RESIZE_HAMMING1: 1.138 + // The Hamming filter takes as much space in the source image in 1.139 + // each direction as the size of the window = 1 for Hamming1. 1.140 + return 1.0f; 1.141 + case ImageOperations::RESIZE_LANCZOS2: 1.142 + // The Lanczos filter takes as much space in the source image in 1.143 + // each direction as the size of the window = 2 for Lanczos2. 1.144 + return 2.0f; 1.145 + case ImageOperations::RESIZE_LANCZOS3: 1.146 + // The Lanczos filter takes as much space in the source image in 1.147 + // each direction as the size of the window = 3 for Lanczos3. 1.148 + return 3.0f; 1.149 + default: 1.150 + return 1.0f; 1.151 + } 1.152 + } 1.153 + 1.154 + // Computes one set of filters either horizontally or vertically. The caller 1.155 + // will specify the "min" and "max" rather than the bottom/top and 1.156 + // right/bottom so that the same code can be re-used in each dimension. 1.157 + // 1.158 + // |src_depend_lo| and |src_depend_size| gives the range for the source 1.159 + // depend rectangle (horizontally or vertically at the caller's discretion 1.160 + // -- see above for what this means). 1.161 + // 1.162 + // Likewise, the range of destination values to compute and the scale factor 1.163 + // for the transform is also specified. 1.164 + void ComputeFilters(int src_size, 1.165 + int dest_subset_lo, int dest_subset_size, 1.166 + float scale, float src_support, 1.167 + ConvolutionFilter1D* output); 1.168 + 1.169 + // Computes the filter value given the coordinate in filter space. 1.170 + inline float ComputeFilter(float pos) { 1.171 + switch (method_) { 1.172 + case ImageOperations::RESIZE_BOX: 1.173 + return EvalBox(pos); 1.174 + case ImageOperations::RESIZE_HAMMING1: 1.175 + return EvalHamming(1, pos); 1.176 + case ImageOperations::RESIZE_LANCZOS2: 1.177 + return EvalLanczos(2, pos); 1.178 + case ImageOperations::RESIZE_LANCZOS3: 1.179 + return EvalLanczos(3, pos); 1.180 + default: 1.181 + return 0; 1.182 + } 1.183 + } 1.184 + 1.185 + ImageOperations::ResizeMethod method_; 1.186 + 1.187 + // Size of the filter support on one side only in the destination space. 1.188 + // See GetFilterSupport. 1.189 + float x_filter_support_; 1.190 + float y_filter_support_; 1.191 + 1.192 + // Subset of scaled destination bitmap to compute. 1.193 + SkIRect out_bounds_; 1.194 + 1.195 + ConvolutionFilter1D x_filter_; 1.196 + ConvolutionFilter1D y_filter_; 1.197 + 1.198 + DISALLOW_COPY_AND_ASSIGN(ResizeFilter); 1.199 +}; 1.200 + 1.201 +ResizeFilter::ResizeFilter(ImageOperations::ResizeMethod method, 1.202 + int src_full_width, int src_full_height, 1.203 + int dest_width, int dest_height, 1.204 + const SkIRect& dest_subset) 1.205 + : method_(method), 1.206 + out_bounds_(dest_subset) { 1.207 + // method_ will only ever refer to an "algorithm method". 1.208 + SkASSERT((ImageOperations::RESIZE_FIRST_ALGORITHM_METHOD <= method) && 1.209 + (method <= ImageOperations::RESIZE_LAST_ALGORITHM_METHOD)); 1.210 + 1.211 + float scale_x = static_cast<float>(dest_width) / 1.212 + static_cast<float>(src_full_width); 1.213 + float scale_y = static_cast<float>(dest_height) / 1.214 + static_cast<float>(src_full_height); 1.215 + 1.216 + x_filter_support_ = GetFilterSupport(scale_x); 1.217 + y_filter_support_ = GetFilterSupport(scale_y); 1.218 + 1.219 + // Support of the filter in source space. 1.220 + float src_x_support = x_filter_support_ / scale_x; 1.221 + float src_y_support = y_filter_support_ / scale_y; 1.222 + 1.223 + ComputeFilters(src_full_width, dest_subset.fLeft, dest_subset.width(), 1.224 + scale_x, src_x_support, &x_filter_); 1.225 + ComputeFilters(src_full_height, dest_subset.fTop, dest_subset.height(), 1.226 + scale_y, src_y_support, &y_filter_); 1.227 +} 1.228 + 1.229 +// TODO(egouriou): Take advantage of periods in the convolution. 1.230 +// Practical resizing filters are periodic outside of the border area. 1.231 +// For Lanczos, a scaling by a (reduced) factor of p/q (q pixels in the 1.232 +// source become p pixels in the destination) will have a period of p. 1.233 +// A nice consequence is a period of 1 when downscaling by an integral 1.234 +// factor. Downscaling from typical display resolutions is also bound 1.235 +// to produce interesting periods as those are chosen to have multiple 1.236 +// small factors. 1.237 +// Small periods reduce computational load and improve cache usage if 1.238 +// the coefficients can be shared. For periods of 1 we can consider 1.239 +// loading the factors only once outside the borders. 1.240 +void ResizeFilter::ComputeFilters(int src_size, 1.241 + int dest_subset_lo, int dest_subset_size, 1.242 + float scale, float src_support, 1.243 + ConvolutionFilter1D* output) { 1.244 + int dest_subset_hi = dest_subset_lo + dest_subset_size; // [lo, hi) 1.245 + 1.246 + // When we're doing a magnification, the scale will be larger than one. This 1.247 + // means the destination pixels are much smaller than the source pixels, and 1.248 + // that the range covered by the filter won't necessarily cover any source 1.249 + // pixel boundaries. Therefore, we use these clamped values (max of 1) for 1.250 + // some computations. 1.251 + float clamped_scale = std::min(1.0f, scale); 1.252 + 1.253 + // Speed up the divisions below by turning them into multiplies. 1.254 + float inv_scale = 1.0f / scale; 1.255 + 1.256 + StackVector<float, 64> filter_values; 1.257 + StackVector<int16_t, 64> fixed_filter_values; 1.258 + 1.259 + // Loop over all pixels in the output range. We will generate one set of 1.260 + // filter values for each one. Those values will tell us how to blend the 1.261 + // source pixels to compute the destination pixel. 1.262 + for (int dest_subset_i = dest_subset_lo; dest_subset_i < dest_subset_hi; 1.263 + dest_subset_i++) { 1.264 + // Reset the arrays. We don't declare them inside so they can re-use the 1.265 + // same malloc-ed buffer. 1.266 + filter_values->clear(); 1.267 + fixed_filter_values->clear(); 1.268 + 1.269 + // This is the pixel in the source directly under the pixel in the dest. 1.270 + // Note that we base computations on the "center" of the pixels. To see 1.271 + // why, observe that the destination pixel at coordinates (0, 0) in a 5.0x 1.272 + // downscale should "cover" the pixels around the pixel with *its center* 1.273 + // at coordinates (2.5, 2.5) in the source, not those around (0, 0). 1.274 + // Hence we need to scale coordinates (0.5, 0.5), not (0, 0). 1.275 + float src_pixel = (static_cast<float>(dest_subset_i) + 0.5f) * inv_scale; 1.276 + 1.277 + // Compute the (inclusive) range of source pixels the filter covers. 1.278 + int src_begin = std::max(0, FloorInt(src_pixel - src_support)); 1.279 + int src_end = std::min(src_size - 1, CeilInt(src_pixel + src_support)); 1.280 + 1.281 + // Compute the unnormalized filter value at each location of the source 1.282 + // it covers. 1.283 + float filter_sum = 0.0f; // Sub of the filter values for normalizing. 1.284 + for (int cur_filter_pixel = src_begin; cur_filter_pixel <= src_end; 1.285 + cur_filter_pixel++) { 1.286 + // Distance from the center of the filter, this is the filter coordinate 1.287 + // in source space. We also need to consider the center of the pixel 1.288 + // when comparing distance against 'src_pixel'. In the 5x downscale 1.289 + // example used above the distance from the center of the filter to 1.290 + // the pixel with coordinates (2, 2) should be 0, because its center 1.291 + // is at (2.5, 2.5). 1.292 + float src_filter_dist = 1.293 + ((static_cast<float>(cur_filter_pixel) + 0.5f) - src_pixel); 1.294 + 1.295 + // Since the filter really exists in dest space, map it there. 1.296 + float dest_filter_dist = src_filter_dist * clamped_scale; 1.297 + 1.298 + // Compute the filter value at that location. 1.299 + float filter_value = ComputeFilter(dest_filter_dist); 1.300 + filter_values->push_back(filter_value); 1.301 + 1.302 + filter_sum += filter_value; 1.303 + } 1.304 + 1.305 + // The filter must be normalized so that we don't affect the brightness of 1.306 + // the image. Convert to normalized fixed point. 1.307 + int16_t fixed_sum = 0; 1.308 + for (size_t i = 0; i < filter_values->size(); i++) { 1.309 + int16_t cur_fixed = output->FloatToFixed(filter_values[i] / filter_sum); 1.310 + fixed_sum += cur_fixed; 1.311 + fixed_filter_values->push_back(cur_fixed); 1.312 + } 1.313 + 1.314 + // The conversion to fixed point will leave some rounding errors, which 1.315 + // we add back in to avoid affecting the brightness of the image. We 1.316 + // arbitrarily add this to the center of the filter array (this won't always 1.317 + // be the center of the filter function since it could get clipped on the 1.318 + // edges, but it doesn't matter enough to worry about that case). 1.319 + int16_t leftovers = output->FloatToFixed(1.0f) - fixed_sum; 1.320 + fixed_filter_values[fixed_filter_values->size() / 2] += leftovers; 1.321 + 1.322 + // Now it's ready to go. 1.323 + output->AddFilter(src_begin, &fixed_filter_values[0], 1.324 + static_cast<int>(fixed_filter_values->size())); 1.325 + } 1.326 + 1.327 + output->PaddingForSIMD(8); 1.328 +} 1.329 + 1.330 +ImageOperations::ResizeMethod ResizeMethodToAlgorithmMethod( 1.331 + ImageOperations::ResizeMethod method) { 1.332 + // Convert any "Quality Method" into an "Algorithm Method" 1.333 + if (method >= ImageOperations::RESIZE_FIRST_ALGORITHM_METHOD && 1.334 + method <= ImageOperations::RESIZE_LAST_ALGORITHM_METHOD) { 1.335 + return method; 1.336 + } 1.337 + // The call to ImageOperationsGtv::Resize() above took care of 1.338 + // GPU-acceleration in the cases where it is possible. So now we just 1.339 + // pick the appropriate software method for each resize quality. 1.340 + switch (method) { 1.341 + // Users of RESIZE_GOOD are willing to trade a lot of quality to 1.342 + // get speed, allowing the use of linear resampling to get hardware 1.343 + // acceleration (SRB). Hence any of our "good" software filters 1.344 + // will be acceptable, and we use the fastest one, Hamming-1. 1.345 + case ImageOperations::RESIZE_GOOD: 1.346 + // Users of RESIZE_BETTER are willing to trade some quality in order 1.347 + // to improve performance, but are guaranteed not to devolve to a linear 1.348 + // resampling. In visual tests we see that Hamming-1 is not as good as 1.349 + // Lanczos-2, however it is about 40% faster and Lanczos-2 itself is 1.350 + // about 30% faster than Lanczos-3. The use of Hamming-1 has been deemed 1.351 + // an acceptable trade-off between quality and speed. 1.352 + case ImageOperations::RESIZE_BETTER: 1.353 + return ImageOperations::RESIZE_HAMMING1; 1.354 + default: 1.355 + return ImageOperations::RESIZE_LANCZOS3; 1.356 + } 1.357 +} 1.358 + 1.359 +} // namespace 1.360 + 1.361 +// Resize ---------------------------------------------------------------------- 1.362 + 1.363 +// static 1.364 +SkBitmap ImageOperations::Resize(const SkBitmap& source, 1.365 + ResizeMethod method, 1.366 + int dest_width, int dest_height, 1.367 + const SkIRect& dest_subset, 1.368 + void* dest_pixels /* = nullptr */) { 1.369 + if (method == ImageOperations::RESIZE_SUBPIXEL) 1.370 + return ResizeSubpixel(source, dest_width, dest_height, dest_subset); 1.371 + else 1.372 + return ResizeBasic(source, method, dest_width, dest_height, dest_subset, 1.373 + dest_pixels); 1.374 +} 1.375 + 1.376 +// static 1.377 +SkBitmap ImageOperations::ResizeSubpixel(const SkBitmap& source, 1.378 + int dest_width, int dest_height, 1.379 + const SkIRect& dest_subset) { 1.380 + // Currently only works on Linux/BSD because these are the only platforms 1.381 + // where SkFontHost::GetSubpixelOrder is defined. 1.382 +#if defined(XP_UNIX) 1.383 + // Understand the display. 1.384 + const SkFontHost::LCDOrder order = SkFontHost::GetSubpixelOrder(); 1.385 + const SkFontHost::LCDOrientation orientation = 1.386 + SkFontHost::GetSubpixelOrientation(); 1.387 + 1.388 + // Decide on which dimension, if any, to deploy subpixel rendering. 1.389 + int w = 1; 1.390 + int h = 1; 1.391 + switch (orientation) { 1.392 + case SkFontHost::kHorizontal_LCDOrientation: 1.393 + w = dest_width < source.width() ? 3 : 1; 1.394 + break; 1.395 + case SkFontHost::kVertical_LCDOrientation: 1.396 + h = dest_height < source.height() ? 3 : 1; 1.397 + break; 1.398 + } 1.399 + 1.400 + // Resize the image. 1.401 + const int width = dest_width * w; 1.402 + const int height = dest_height * h; 1.403 + SkIRect subset = { dest_subset.fLeft, dest_subset.fTop, 1.404 + dest_subset.fLeft + dest_subset.width() * w, 1.405 + dest_subset.fTop + dest_subset.height() * h }; 1.406 + SkBitmap img = ResizeBasic(source, ImageOperations::RESIZE_LANCZOS3, width, 1.407 + height, subset); 1.408 + const int row_words = img.rowBytes() / 4; 1.409 + if (w == 1 && h == 1) 1.410 + return img; 1.411 + 1.412 + // Render into subpixels. 1.413 + SkBitmap result; 1.414 + result.setConfig(SkBitmap::kARGB_8888_Config, dest_subset.width(), 1.415 + dest_subset.height()); 1.416 + result.allocPixels(); 1.417 + if (!result.readyToDraw()) 1.418 + return img; 1.419 + 1.420 + SkAutoLockPixels locker(img); 1.421 + if (!img.readyToDraw()) 1.422 + return img; 1.423 + 1.424 + uint32_t* src_row = img.getAddr32(0, 0); 1.425 + uint32_t* dst_row = result.getAddr32(0, 0); 1.426 + for (int y = 0; y < dest_subset.height(); y++) { 1.427 + uint32_t* src = src_row; 1.428 + uint32_t* dst = dst_row; 1.429 + for (int x = 0; x < dest_subset.width(); x++, src += w, dst++) { 1.430 + uint8_t r = 0, g = 0, b = 0, a = 0; 1.431 + switch (order) { 1.432 + case SkFontHost::kRGB_LCDOrder: 1.433 + switch (orientation) { 1.434 + case SkFontHost::kHorizontal_LCDOrientation: 1.435 + r = SkGetPackedR32(src[0]); 1.436 + g = SkGetPackedG32(src[1]); 1.437 + b = SkGetPackedB32(src[2]); 1.438 + a = SkGetPackedA32(src[1]); 1.439 + break; 1.440 + case SkFontHost::kVertical_LCDOrientation: 1.441 + r = SkGetPackedR32(src[0 * row_words]); 1.442 + g = SkGetPackedG32(src[1 * row_words]); 1.443 + b = SkGetPackedB32(src[2 * row_words]); 1.444 + a = SkGetPackedA32(src[1 * row_words]); 1.445 + break; 1.446 + } 1.447 + break; 1.448 + case SkFontHost::kBGR_LCDOrder: 1.449 + switch (orientation) { 1.450 + case SkFontHost::kHorizontal_LCDOrientation: 1.451 + b = SkGetPackedB32(src[0]); 1.452 + g = SkGetPackedG32(src[1]); 1.453 + r = SkGetPackedR32(src[2]); 1.454 + a = SkGetPackedA32(src[1]); 1.455 + break; 1.456 + case SkFontHost::kVertical_LCDOrientation: 1.457 + b = SkGetPackedB32(src[0 * row_words]); 1.458 + g = SkGetPackedG32(src[1 * row_words]); 1.459 + r = SkGetPackedR32(src[2 * row_words]); 1.460 + a = SkGetPackedA32(src[1 * row_words]); 1.461 + break; 1.462 + } 1.463 + break; 1.464 + case SkFontHost::kNONE_LCDOrder: 1.465 + break; 1.466 + } 1.467 + // Premultiplied alpha is very fragile. 1.468 + a = a > r ? a : r; 1.469 + a = a > g ? a : g; 1.470 + a = a > b ? a : b; 1.471 + *dst = SkPackARGB32(a, r, g, b); 1.472 + } 1.473 + src_row += h * row_words; 1.474 + dst_row += result.rowBytes() / 4; 1.475 + } 1.476 + result.setAlphaType(img.alphaType()); 1.477 + return result; 1.478 +#else 1.479 + return SkBitmap(); 1.480 +#endif // OS_POSIX && !OS_MACOSX && !defined(OS_ANDROID) 1.481 +} 1.482 + 1.483 +// static 1.484 +SkBitmap ImageOperations::ResizeBasic(const SkBitmap& source, 1.485 + ResizeMethod method, 1.486 + int dest_width, int dest_height, 1.487 + const SkIRect& dest_subset, 1.488 + void* dest_pixels /* = nullptr */) { 1.489 + // Ensure that the ResizeMethod enumeration is sound. 1.490 + SkASSERT(((RESIZE_FIRST_QUALITY_METHOD <= method) && 1.491 + (method <= RESIZE_LAST_QUALITY_METHOD)) || 1.492 + ((RESIZE_FIRST_ALGORITHM_METHOD <= method) && 1.493 + (method <= RESIZE_LAST_ALGORITHM_METHOD))); 1.494 + 1.495 + // If the size of source or destination is 0, i.e. 0x0, 0xN or Nx0, just 1.496 + // return empty. 1.497 + if (source.width() < 1 || source.height() < 1 || 1.498 + dest_width < 1 || dest_height < 1) 1.499 + return SkBitmap(); 1.500 + 1.501 + method = ResizeMethodToAlgorithmMethod(method); 1.502 + // Check that we deal with an "algorithm methods" from this point onward. 1.503 + SkASSERT((ImageOperations::RESIZE_FIRST_ALGORITHM_METHOD <= method) && 1.504 + (method <= ImageOperations::RESIZE_LAST_ALGORITHM_METHOD)); 1.505 + 1.506 + SkAutoLockPixels locker(source); 1.507 + if (!source.readyToDraw()) 1.508 + return SkBitmap(); 1.509 + 1.510 + ResizeFilter filter(method, source.width(), source.height(), 1.511 + dest_width, dest_height, dest_subset); 1.512 + 1.513 + // Get a source bitmap encompassing this touched area. We construct the 1.514 + // offsets and row strides such that it looks like a new bitmap, while 1.515 + // referring to the old data. 1.516 + const uint8_t* source_subset = 1.517 + reinterpret_cast<const uint8_t*>(source.getPixels()); 1.518 + 1.519 + // Convolve into the result. 1.520 + SkBitmap result; 1.521 + result.setConfig(SkBitmap::kARGB_8888_Config, 1.522 + dest_subset.width(), dest_subset.height()); 1.523 + 1.524 + if (dest_pixels) { 1.525 + result.setPixels(dest_pixels); 1.526 + } else { 1.527 + result.allocPixels(); 1.528 + } 1.529 + 1.530 + if (!result.readyToDraw()) 1.531 + return SkBitmap(); 1.532 + 1.533 + BGRAConvolve2D(source_subset, static_cast<int>(source.rowBytes()), 1.534 + !source.isOpaque(), filter.x_filter(), filter.y_filter(), 1.535 + static_cast<int>(result.rowBytes()), 1.536 + static_cast<unsigned char*>(result.getPixels()), 1.537 + /* sse = */ false); 1.538 + 1.539 + // Preserve the "opaque" flag for use as an optimization later. 1.540 + result.setAlphaType(source.alphaType()); 1.541 + 1.542 + return result; 1.543 +} 1.544 + 1.545 +// static 1.546 +SkBitmap ImageOperations::Resize(const SkBitmap& source, 1.547 + ResizeMethod method, 1.548 + int dest_width, int dest_height, 1.549 + void* dest_pixels /* = nullptr */) { 1.550 + SkIRect dest_subset = { 0, 0, dest_width, dest_height }; 1.551 + return Resize(source, method, dest_width, dest_height, dest_subset, 1.552 + dest_pixels); 1.553 +} 1.554 + 1.555 +} // namespace skia