gfx/2d/image_operations.cpp

changeset 0
6474c204b198
     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

mercurial