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