15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define _USE_MATH_DEFINES
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <algorithm>
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <cmath>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <limits>
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "skia/ext/image_operations.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TODO(pkasting): skia/ext should not depend on base/!
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/containers/stack_container.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/debug/trace_event.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/metrics/histogram.h"
17eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/time/time.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "build/build_config.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "skia/ext/convolver.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/skia/include/core/SkColorPriv.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/skia/include/core/SkFontHost.h"
22eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "third_party/skia/include/core/SkRect.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace skia {
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Returns the ceiling/floor as an integer.
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)inline int CeilInt(float val) {
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return static_cast<int>(ceil(val));
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)inline int FloorInt(float val) {
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return static_cast<int>(floor(val));
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Filter function computation -------------------------------------------------
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Evaluates the box filter, which goes from -0.5 to +0.5.
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)float EvalBox(float x) {
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return (x >= -0.5f && x < 0.5f) ? 1.0f : 0.0f;
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Evaluates the Lanczos filter of the given filter size window for the given
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// position.
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// |filter_size| is the width of the filter (the "window"), outside of which
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// the value of the function is 0. Inside of the window, the value is the
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// normalized sinc function:
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//   lanczos(x) = sinc(x) * sinc(x / filter_size);
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// where
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//   sinc(x) = sin(pi*x) / (pi*x);
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)float EvalLanczos(int filter_size, float x) {
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (x <= -filter_size || x >= filter_size)
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0.0f;  // Outside of the window.
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (x > -std::numeric_limits<float>::epsilon() &&
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      x < std::numeric_limits<float>::epsilon())
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 1.0f;  // Special case the discontinuity at the origin.
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  float xpi = x * static_cast<float>(M_PI);
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return (sin(xpi) / xpi) *  // sinc(x)
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          sin(xpi / filter_size) / (xpi / filter_size);  // sinc(x/filter_size)
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Evaluates the Hamming filter of the given filter size window for the given
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// position.
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The filter covers [-filter_size, +filter_size]. Outside of this window
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// the value of the function is 0. Inside of the window, the value is sinus
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// cardinal multiplied by a recentered Hamming function. The traditional
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Hamming formula for a window of size N and n ranging in [0, N-1] is:
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//   hamming(n) = 0.54 - 0.46 * cos(2 * pi * n / (N-1)))
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// In our case we want the function centered for x == 0 and at its minimum
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// on both ends of the window (x == +/- filter_size), hence the adjusted
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// formula:
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//   hamming(x) = (0.54 -
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//                 0.46 * cos(2 * pi * (x - filter_size)/ (2 * filter_size)))
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//              = 0.54 - 0.46 * cos(pi * x / filter_size - pi)
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//              = 0.54 + 0.46 * cos(pi * x / filter_size)
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)float EvalHamming(int filter_size, float x) {
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (x <= -filter_size || x >= filter_size)
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0.0f;  // Outside of the window.
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (x > -std::numeric_limits<float>::epsilon() &&
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      x < std::numeric_limits<float>::epsilon())
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 1.0f;  // Special case the sinc discontinuity at the origin.
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const float xpi = x * static_cast<float>(M_PI);
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ((sin(xpi) / xpi) *  // sinc(x)
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          (0.54f + 0.46f * cos(xpi / filter_size)));  // hamming(x)
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// ResizeFilter ----------------------------------------------------------------
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Encapsulates computation and storage of the filters required for one complete
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// resize operation.
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class ResizeFilter {
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ResizeFilter(ImageOperations::ResizeMethod method,
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               int src_full_width, int src_full_height,
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               int dest_width, int dest_height,
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               const SkIRect& dest_subset);
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Returns the filled filter values.
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const ConvolutionFilter1D& x_filter() { return x_filter_; }
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const ConvolutionFilter1D& y_filter() { return y_filter_; }
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Returns the number of pixels that the filer spans, in filter space (the
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // destination image).
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  float GetFilterSupport(float scale) {
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    switch (method_) {
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case ImageOperations::RESIZE_BOX:
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // The box filter just scales with the image scaling.
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return 0.5f;  // Only want one side of the filter = /2.
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case ImageOperations::RESIZE_HAMMING1:
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // The Hamming filter takes as much space in the source image in
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // each direction as the size of the window = 1 for Hamming1.
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return 1.0f;
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case ImageOperations::RESIZE_LANCZOS2:
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // The Lanczos filter takes as much space in the source image in
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // each direction as the size of the window = 2 for Lanczos2.
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return 2.0f;
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case ImageOperations::RESIZE_LANCZOS3:
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // The Lanczos filter takes as much space in the source image in
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // each direction as the size of the window = 3 for Lanczos3.
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return 3.0f;
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      default:
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        NOTREACHED();
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return 1.0f;
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Computes one set of filters either horizontally or vertically. The caller
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // will specify the "min" and "max" rather than the bottom/top and
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // right/bottom so that the same code can be re-used in each dimension.
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // |src_depend_lo| and |src_depend_size| gives the range for the source
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // depend rectangle (horizontally or vertically at the caller's discretion
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // -- see above for what this means).
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Likewise, the range of destination values to compute and the scale factor
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // for the transform is also specified.
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void ComputeFilters(int src_size,
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      int dest_subset_lo, int dest_subset_size,
143c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                      float scale,
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      ConvolutionFilter1D* output);
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Computes the filter value given the coordinate in filter space.
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  inline float ComputeFilter(float pos) {
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    switch (method_) {
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case ImageOperations::RESIZE_BOX:
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return EvalBox(pos);
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case ImageOperations::RESIZE_HAMMING1:
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return EvalHamming(1, pos);
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case ImageOperations::RESIZE_LANCZOS2:
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return EvalLanczos(2, pos);
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case ImageOperations::RESIZE_LANCZOS3:
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return EvalLanczos(3, pos);
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      default:
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        NOTREACHED();
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return 0;
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ImageOperations::ResizeMethod method_;
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Size of the filter support on one side only in the destination space.
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // See GetFilterSupport.
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  float x_filter_support_;
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  float y_filter_support_;
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Subset of scaled destination bitmap to compute.
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkIRect out_bounds_;
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ConvolutionFilter1D x_filter_;
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ConvolutionFilter1D y_filter_;
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(ResizeFilter);
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ResizeFilter::ResizeFilter(ImageOperations::ResizeMethod method,
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           int src_full_width, int src_full_height,
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           int dest_width, int dest_height,
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           const SkIRect& dest_subset)
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : method_(method),
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      out_bounds_(dest_subset) {
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // method_ will only ever refer to an "algorithm method".
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkASSERT((ImageOperations::RESIZE_FIRST_ALGORITHM_METHOD <= method) &&
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           (method <= ImageOperations::RESIZE_LAST_ALGORITHM_METHOD));
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  float scale_x = static_cast<float>(dest_width) /
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  static_cast<float>(src_full_width);
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  float scale_y = static_cast<float>(dest_height) /
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  static_cast<float>(src_full_height);
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ComputeFilters(src_full_width, dest_subset.fLeft, dest_subset.width(),
195c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                 scale_x, &x_filter_);
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ComputeFilters(src_full_height, dest_subset.fTop, dest_subset.height(),
197c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                 scale_y, &y_filter_);
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TODO(egouriou): Take advantage of periods in the convolution.
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Practical resizing filters are periodic outside of the border area.
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// For Lanczos, a scaling by a (reduced) factor of p/q (q pixels in the
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// source become p pixels in the destination) will have a period of p.
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// A nice consequence is a period of 1 when downscaling by an integral
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// factor. Downscaling from typical display resolutions is also bound
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// to produce interesting periods as those are chosen to have multiple
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// small factors.
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Small periods reduce computational load and improve cache usage if
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// the coefficients can be shared. For periods of 1 we can consider
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// loading the factors only once outside the borders.
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ResizeFilter::ComputeFilters(int src_size,
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  int dest_subset_lo, int dest_subset_size,
213c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                  float scale,
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  ConvolutionFilter1D* output) {
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int dest_subset_hi = dest_subset_lo + dest_subset_size;  // [lo, hi)
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // When we're doing a magnification, the scale will be larger than one. This
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // means the destination pixels are much smaller than the source pixels, and
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // that the range covered by the filter won't necessarily cover any source
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // pixel boundaries. Therefore, we use these clamped values (max of 1) for
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // some computations.
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  float clamped_scale = std::min(1.0f, scale);
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
224c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // This is how many source pixels from the center we need to count
225c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // to support the filtering function.
226c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  float src_support = GetFilterSupport(clamped_scale) / clamped_scale;
227c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Speed up the divisions below by turning them into multiplies.
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  float inv_scale = 1.0f / scale;
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::StackVector<float, 64> filter_values;
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::StackVector<int16, 64> fixed_filter_values;
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Loop over all pixels in the output range. We will generate one set of
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // filter values for each one. Those values will tell us how to blend the
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // source pixels to compute the destination pixel.
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (int dest_subset_i = dest_subset_lo; dest_subset_i < dest_subset_hi;
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       dest_subset_i++) {
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Reset the arrays. We don't declare them inside so they can re-use the
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // same malloc-ed buffer.
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    filter_values->clear();
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fixed_filter_values->clear();
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // This is the pixel in the source directly under the pixel in the dest.
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Note that we base computations on the "center" of the pixels. To see
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // why, observe that the destination pixel at coordinates (0, 0) in a 5.0x
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // downscale should "cover" the pixels around the pixel with *its center*
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // at coordinates (2.5, 2.5) in the source, not those around (0, 0).
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Hence we need to scale coordinates (0.5, 0.5), not (0, 0).
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    float src_pixel = (static_cast<float>(dest_subset_i) + 0.5f) * inv_scale;
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Compute the (inclusive) range of source pixels the filter covers.
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int src_begin = std::max(0, FloorInt(src_pixel - src_support));
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int src_end = std::min(src_size - 1, CeilInt(src_pixel + src_support));
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Compute the unnormalized filter value at each location of the source
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // it covers.
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    float filter_sum = 0.0f;  // Sub of the filter values for normalizing.
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (int cur_filter_pixel = src_begin; cur_filter_pixel <= src_end;
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         cur_filter_pixel++) {
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Distance from the center of the filter, this is the filter coordinate
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // in source space. We also need to consider the center of the pixel
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // when comparing distance against 'src_pixel'. In the 5x downscale
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // example used above the distance from the center of the filter to
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // the pixel with coordinates (2, 2) should be 0, because its center
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // is at (2.5, 2.5).
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      float src_filter_dist =
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          ((static_cast<float>(cur_filter_pixel) + 0.5f) - src_pixel);
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Since the filter really exists in dest space, map it there.
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      float dest_filter_dist = src_filter_dist * clamped_scale;
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Compute the filter value at that location.
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      float filter_value = ComputeFilter(dest_filter_dist);
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      filter_values->push_back(filter_value);
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      filter_sum += filter_value;
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(!filter_values->empty()) << "We should always get a filter!";
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // The filter must be normalized so that we don't affect the brightness of
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // the image. Convert to normalized fixed point.
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int16 fixed_sum = 0;
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (size_t i = 0; i < filter_values->size(); i++) {
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int16 cur_fixed = output->FloatToFixed(filter_values[i] / filter_sum);
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      fixed_sum += cur_fixed;
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      fixed_filter_values->push_back(cur_fixed);
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // The conversion to fixed point will leave some rounding errors, which
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // we add back in to avoid affecting the brightness of the image. We
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // arbitrarily add this to the center of the filter array (this won't always
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // be the center of the filter function since it could get clipped on the
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // edges, but it doesn't matter enough to worry about that case).
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int16 leftovers = output->FloatToFixed(1.0f) - fixed_sum;
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fixed_filter_values[fixed_filter_values->size() / 2] += leftovers;
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Now it's ready to go.
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    output->AddFilter(src_begin, &fixed_filter_values[0],
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      static_cast<int>(fixed_filter_values->size()));
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
303c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  output->PaddingForSIMD();
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageOperations::ResizeMethod ResizeMethodToAlgorithmMethod(
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ImageOperations::ResizeMethod method) {
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Convert any "Quality Method" into an "Algorithm Method"
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (method >= ImageOperations::RESIZE_FIRST_ALGORITHM_METHOD &&
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      method <= ImageOperations::RESIZE_LAST_ALGORITHM_METHOD) {
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return method;
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The call to ImageOperationsGtv::Resize() above took care of
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // GPU-acceleration in the cases where it is possible. So now we just
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // pick the appropriate software method for each resize quality.
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (method) {
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Users of RESIZE_GOOD are willing to trade a lot of quality to
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // get speed, allowing the use of linear resampling to get hardware
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // acceleration (SRB). Hence any of our "good" software filters
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // will be acceptable, and we use the fastest one, Hamming-1.
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ImageOperations::RESIZE_GOOD:
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Users of RESIZE_BETTER are willing to trade some quality in order
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // to improve performance, but are guaranteed not to devolve to a linear
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // resampling. In visual tests we see that Hamming-1 is not as good as
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Lanczos-2, however it is about 40% faster and Lanczos-2 itself is
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // about 30% faster than Lanczos-3. The use of Hamming-1 has been deemed
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // an acceptable trade-off between quality and speed.
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ImageOperations::RESIZE_BETTER:
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return ImageOperations::RESIZE_HAMMING1;
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return ImageOperations::RESIZE_LANCZOS3;
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Resize ----------------------------------------------------------------------
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SkBitmap ImageOperations::Resize(const SkBitmap& source,
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 ResizeMethod method,
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 int dest_width, int dest_height,
3432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                 const SkIRect& dest_subset,
3442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                 SkBitmap::Allocator* allocator) {
3452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (method == ImageOperations::RESIZE_SUBPIXEL) {
3462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return ResizeSubpixel(source, dest_width, dest_height,
3472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                          dest_subset, allocator);
3482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else {
3492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return ResizeBasic(source, method, dest_width, dest_height, dest_subset,
3502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                       allocator);
3512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SkBitmap ImageOperations::ResizeSubpixel(const SkBitmap& source,
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                         int dest_width, int dest_height,
3572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                         const SkIRect& dest_subset,
3582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                         SkBitmap::Allocator* allocator) {
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TRACE_EVENT2("skia", "ImageOperations::ResizeSubpixel",
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               "src_pixels", source.width()*source.height(),
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               "dst_pixels", dest_width*dest_height);
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Currently only works on Linux/BSD because these are the only platforms
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // where SkFontHost::GetSubpixelOrder is defined.
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_LINUX) && !defined(GTV)
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Understand the display.
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const SkFontHost::LCDOrder order = SkFontHost::GetSubpixelOrder();
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const SkFontHost::LCDOrientation orientation =
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SkFontHost::GetSubpixelOrientation();
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Decide on which dimension, if any, to deploy subpixel rendering.
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int w = 1;
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int h = 1;
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (orientation) {
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case SkFontHost::kHorizontal_LCDOrientation:
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      w = dest_width < source.width() ? 3 : 1;
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case SkFontHost::kVertical_LCDOrientation:
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      h = dest_height < source.height() ? 3 : 1;
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Resize the image.
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int width = dest_width * w;
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int height = dest_height * h;
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkIRect subset = { dest_subset.fLeft, dest_subset.fTop,
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     dest_subset.fLeft + dest_subset.width() * w,
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     dest_subset.fTop + dest_subset.height() * h };
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkBitmap img = ResizeBasic(source, ImageOperations::RESIZE_LANCZOS3, width,
3892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                             height, subset, allocator);
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int row_words = img.rowBytes() / 4;
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (w == 1 && h == 1)
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return img;
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Render into subpixels.
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkBitmap result;
396f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  result.setInfo(SkImageInfo::MakeN32(dest_subset.width(), dest_subset.height(),
397f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                      img.alphaType()));
3982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  result.allocPixels(allocator, NULL);
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!result.readyToDraw())
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return img;
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkAutoLockPixels locker(img);
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!img.readyToDraw())
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return img;
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  uint32* src_row = img.getAddr32(0, 0);
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  uint32* dst_row = result.getAddr32(0, 0);
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (int y = 0; y < dest_subset.height(); y++) {
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    uint32* src = src_row;
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    uint32* dst = dst_row;
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (int x = 0; x < dest_subset.width(); x++, src += w, dst++) {
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      uint8 r = 0, g = 0, b = 0, a = 0;
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      switch (order) {
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        case SkFontHost::kRGB_LCDOrder:
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          switch (orientation) {
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            case SkFontHost::kHorizontal_LCDOrientation:
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              r = SkGetPackedR32(src[0]);
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              g = SkGetPackedG32(src[1]);
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              b = SkGetPackedB32(src[2]);
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              a = SkGetPackedA32(src[1]);
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              break;
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            case SkFontHost::kVertical_LCDOrientation:
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              r = SkGetPackedR32(src[0 * row_words]);
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              g = SkGetPackedG32(src[1 * row_words]);
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              b = SkGetPackedB32(src[2 * row_words]);
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              a = SkGetPackedA32(src[1 * row_words]);
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              break;
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          }
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          break;
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        case SkFontHost::kBGR_LCDOrder:
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          switch (orientation) {
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            case SkFontHost::kHorizontal_LCDOrientation:
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              b = SkGetPackedB32(src[0]);
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              g = SkGetPackedG32(src[1]);
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              r = SkGetPackedR32(src[2]);
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              a = SkGetPackedA32(src[1]);
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              break;
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            case SkFontHost::kVertical_LCDOrientation:
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              b = SkGetPackedB32(src[0 * row_words]);
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              g = SkGetPackedG32(src[1 * row_words]);
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              r = SkGetPackedR32(src[2 * row_words]);
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              a = SkGetPackedA32(src[1 * row_words]);
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              break;
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          }
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          break;
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        case SkFontHost::kNONE_LCDOrder:
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          NOTREACHED();
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Premultiplied alpha is very fragile.
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      a = a > r ? a : r;
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      a = a > g ? a : g;
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      a = a > b ? a : b;
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      *dst = SkPackARGB32(a, r, g, b);
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    src_row += h * row_words;
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    dst_row += result.rowBytes() / 4;
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return result;
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#else
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return SkBitmap();
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif  // OS_POSIX && !OS_MACOSX && !defined(OS_ANDROID)
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SkBitmap ImageOperations::ResizeBasic(const SkBitmap& source,
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      ResizeMethod method,
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      int dest_width, int dest_height,
4682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                      const SkIRect& dest_subset,
4692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                      SkBitmap::Allocator* allocator) {
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TRACE_EVENT2("skia", "ImageOperations::ResizeBasic",
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               "src_pixels", source.width()*source.height(),
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               "dst_pixels", dest_width*dest_height);
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Ensure that the ResizeMethod enumeration is sound.
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkASSERT(((RESIZE_FIRST_QUALITY_METHOD <= method) &&
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            (method <= RESIZE_LAST_QUALITY_METHOD)) ||
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           ((RESIZE_FIRST_ALGORITHM_METHOD <= method) &&
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            (method <= RESIZE_LAST_ALGORITHM_METHOD)));
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Time how long this takes to see if it's a problem for users.
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::TimeTicks resize_start = base::TimeTicks::Now();
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkIRect dest = { 0, 0, dest_width, dest_height };
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(dest.contains(dest_subset)) <<
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "The supplied subset does not fall within the destination image.";
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If the size of source or destination is 0, i.e. 0x0, 0xN or Nx0, just
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // return empty.
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (source.width() < 1 || source.height() < 1 ||
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      dest_width < 1 || dest_height < 1)
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return SkBitmap();
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  method = ResizeMethodToAlgorithmMethod(method);
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Check that we deal with an "algorithm methods" from this point onward.
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkASSERT((ImageOperations::RESIZE_FIRST_ALGORITHM_METHOD <= method) &&
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           (method <= ImageOperations::RESIZE_LAST_ALGORITHM_METHOD));
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkAutoLockPixels locker(source);
498116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (!source.readyToDraw() || source.colorType() != kN32_SkColorType)
4997d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    return SkBitmap();
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ResizeFilter filter(method, source.width(), source.height(),
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      dest_width, dest_height, dest_subset);
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Get a source bitmap encompassing this touched area. We construct the
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // offsets and row strides such that it looks like a new bitmap, while
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // referring to the old data.
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const uint8* source_subset =
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      reinterpret_cast<const uint8*>(source.getPixels());
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Convolve into the result.
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkBitmap result;
512f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  result.setInfo(SkImageInfo::MakeN32(dest_subset.width(), dest_subset.height(), source.alphaType()));
5132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  result.allocPixels(allocator, NULL);
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!result.readyToDraw())
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return SkBitmap();
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BGRAConvolve2D(source_subset, static_cast<int>(source.rowBytes()),
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 !source.isOpaque(), filter.x_filter(), filter.y_filter(),
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 static_cast<int>(result.rowBytes()),
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 static_cast<unsigned char*>(result.getPixels()),
521c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                 true);
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::TimeDelta delta = base::TimeTicks::Now() - resize_start;
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UMA_HISTOGRAM_TIMES("Image.ResampleMS", delta);
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return result;
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SkBitmap ImageOperations::Resize(const SkBitmap& source,
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 ResizeMethod method,
5322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                 int dest_width, int dest_height,
5332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                 SkBitmap::Allocator* allocator) {
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkIRect dest_subset = { 0, 0, dest_width, dest_height };
5352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return Resize(source, method, dest_width, dest_height, dest_subset,
5362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                allocator);
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace skia
540