1ecfff21bde1f0ca3c36533eded325066b5f2d42ddandov/* 2ecfff21bde1f0ca3c36533eded325066b5f2d42ddandov * Copyright 2014 Google Inc. 3ecfff21bde1f0ca3c36533eded325066b5f2d42ddandov * 4ecfff21bde1f0ca3c36533eded325066b5f2d42ddandov * Use of this source code is governed by a BSD-style license that can be 5ecfff21bde1f0ca3c36533eded325066b5f2d42ddandov * found in the LICENSE file. 6ecfff21bde1f0ca3c36533eded325066b5f2d42ddandov */ 7ecfff21bde1f0ca3c36533eded325066b5f2d42ddandov 8ecfff21bde1f0ca3c36533eded325066b5f2d42ddandov#include "SkPatchUtils.h" 9ecfff21bde1f0ca3c36533eded325066b5f2d42ddandov 10b3c9d1c33caf325aada244204215eb790c228c12dandov#include "SkColorPriv.h" 11435071e8ab865e395bec34ef128a412ceb50f063Mike Reed#include "SkColorSpace_Base.h" 12b3c9d1c33caf325aada244204215eb790c228c12dandov#include "SkGeometry.h" 13435071e8ab865e395bec34ef128a412ceb50f063Mike Reed#include "SkPM4f.h" 14b3c9d1c33caf325aada244204215eb790c228c12dandov 154ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reednamespace { 164ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed enum CubicCtrlPts { 174ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed kTopP0_CubicCtrlPts = 0, 184ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed kTopP1_CubicCtrlPts = 1, 194ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed kTopP2_CubicCtrlPts = 2, 204ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed kTopP3_CubicCtrlPts = 3, 214ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed 224ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed kRightP0_CubicCtrlPts = 3, 234ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed kRightP1_CubicCtrlPts = 4, 244ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed kRightP2_CubicCtrlPts = 5, 254ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed kRightP3_CubicCtrlPts = 6, 264ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed 274ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed kBottomP0_CubicCtrlPts = 9, 284ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed kBottomP1_CubicCtrlPts = 8, 294ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed kBottomP2_CubicCtrlPts = 7, 304ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed kBottomP3_CubicCtrlPts = 6, 314ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed 324ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed kLeftP0_CubicCtrlPts = 0, 334ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed kLeftP1_CubicCtrlPts = 11, 344ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed kLeftP2_CubicCtrlPts = 10, 354ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed kLeftP3_CubicCtrlPts = 9, 364ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed }; 374ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed 384ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed // Enum for corner also clockwise. 394ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed enum Corner { 404ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed kTopLeft_Corner = 0, 414ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed kTopRight_Corner, 424ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed kBottomRight_Corner, 434ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed kBottomLeft_Corner 444ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed }; 454ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed} 464ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed 47b3c9d1c33caf325aada244204215eb790c228c12dandov/** 48b3c9d1c33caf325aada244204215eb790c228c12dandov * Evaluator to sample the values of a cubic bezier using forward differences. 49b3c9d1c33caf325aada244204215eb790c228c12dandov * Forward differences is a method for evaluating a nth degree polynomial at a uniform step by only 50b3c9d1c33caf325aada244204215eb790c228c12dandov * adding precalculated values. 51b3c9d1c33caf325aada244204215eb790c228c12dandov * For a linear example we have the function f(t) = m*t+b, then the value of that function at t+h 52b3c9d1c33caf325aada244204215eb790c228c12dandov * would be f(t+h) = m*(t+h)+b. If we want to know the uniform step that we must add to the first 53b3c9d1c33caf325aada244204215eb790c228c12dandov * evaluation f(t) then we need to substract f(t+h) - f(t) = m*t + m*h + b - m*t + b = mh. After 54b3c9d1c33caf325aada244204215eb790c228c12dandov * obtaining this value (mh) we could just add this constant step to our first sampled point 55b3c9d1c33caf325aada244204215eb790c228c12dandov * to compute the next one. 56b3c9d1c33caf325aada244204215eb790c228c12dandov * 57b3c9d1c33caf325aada244204215eb790c228c12dandov * For the cubic case the first difference gives as a result a quadratic polynomial to which we can 58b3c9d1c33caf325aada244204215eb790c228c12dandov * apply again forward differences and get linear function to which we can apply again forward 59b3c9d1c33caf325aada244204215eb790c228c12dandov * differences to get a constant difference. This is why we keep an array of size 4, the 0th 60b3c9d1c33caf325aada244204215eb790c228c12dandov * position keeps the sampled value while the next ones keep the quadratic, linear and constant 61b3c9d1c33caf325aada244204215eb790c228c12dandov * difference values. 62b3c9d1c33caf325aada244204215eb790c228c12dandov */ 63b3c9d1c33caf325aada244204215eb790c228c12dandov 64b3c9d1c33caf325aada244204215eb790c228c12dandovclass FwDCubicEvaluator { 659d524f22bfde5dc3dc8f48e1be39bdebd3bb0304halcanary 66b3c9d1c33caf325aada244204215eb790c228c12dandovpublic: 679d524f22bfde5dc3dc8f48e1be39bdebd3bb0304halcanary 68b3c9d1c33caf325aada244204215eb790c228c12dandov /** 69b3c9d1c33caf325aada244204215eb790c228c12dandov * Receives the 4 control points of the cubic bezier. 70b3c9d1c33caf325aada244204215eb790c228c12dandov */ 719d524f22bfde5dc3dc8f48e1be39bdebd3bb0304halcanary 725ba2b9612ae4bc3a244bf45f1ec55c3a5a41e181caryclark explicit FwDCubicEvaluator(const SkPoint points[4]) 735ba2b9612ae4bc3a244bf45f1ec55c3a5a41e181caryclark : fCoefs(points) { 74b3c9d1c33caf325aada244204215eb790c228c12dandov memcpy(fPoints, points, 4 * sizeof(SkPoint)); 759d524f22bfde5dc3dc8f48e1be39bdebd3bb0304halcanary 76b3c9d1c33caf325aada244204215eb790c228c12dandov this->restart(1); 77b3c9d1c33caf325aada244204215eb790c228c12dandov } 789d524f22bfde5dc3dc8f48e1be39bdebd3bb0304halcanary 79b3c9d1c33caf325aada244204215eb790c228c12dandov /** 80b3c9d1c33caf325aada244204215eb790c228c12dandov * Restarts the forward differences evaluator to the first value of t = 0. 81b3c9d1c33caf325aada244204215eb790c228c12dandov */ 82b3c9d1c33caf325aada244204215eb790c228c12dandov void restart(int divisions) { 83b3c9d1c33caf325aada244204215eb790c228c12dandov fDivisions = divisions; 84b3c9d1c33caf325aada244204215eb790c228c12dandov fCurrent = 0; 85b3c9d1c33caf325aada244204215eb790c228c12dandov fMax = fDivisions + 1; 865ba2b9612ae4bc3a244bf45f1ec55c3a5a41e181caryclark Sk2s h = Sk2s(1.f / fDivisions); 875ba2b9612ae4bc3a244bf45f1ec55c3a5a41e181caryclark Sk2s h2 = h * h; 885ba2b9612ae4bc3a244bf45f1ec55c3a5a41e181caryclark Sk2s h3 = h2 * h; 895ba2b9612ae4bc3a244bf45f1ec55c3a5a41e181caryclark Sk2s fwDiff3 = Sk2s(6) * fCoefs.fA * h3; 905ba2b9612ae4bc3a244bf45f1ec55c3a5a41e181caryclark fFwDiff[3] = to_point(fwDiff3); 915ba2b9612ae4bc3a244bf45f1ec55c3a5a41e181caryclark fFwDiff[2] = to_point(fwDiff3 + times_2(fCoefs.fB) * h2); 925ba2b9612ae4bc3a244bf45f1ec55c3a5a41e181caryclark fFwDiff[1] = to_point(fCoefs.fA * h3 + fCoefs.fB * h2 + fCoefs.fC * h); 935ba2b9612ae4bc3a244bf45f1ec55c3a5a41e181caryclark fFwDiff[0] = to_point(fCoefs.fD); 94b3c9d1c33caf325aada244204215eb790c228c12dandov } 959d524f22bfde5dc3dc8f48e1be39bdebd3bb0304halcanary 96b3c9d1c33caf325aada244204215eb790c228c12dandov /** 97b3c9d1c33caf325aada244204215eb790c228c12dandov * Check if the evaluator is still within the range of 0<=t<=1 98b3c9d1c33caf325aada244204215eb790c228c12dandov */ 99b3c9d1c33caf325aada244204215eb790c228c12dandov bool done() const { 100b3c9d1c33caf325aada244204215eb790c228c12dandov return fCurrent > fMax; 101b3c9d1c33caf325aada244204215eb790c228c12dandov } 1029d524f22bfde5dc3dc8f48e1be39bdebd3bb0304halcanary 103b3c9d1c33caf325aada244204215eb790c228c12dandov /** 104b3c9d1c33caf325aada244204215eb790c228c12dandov * Call next to obtain the SkPoint sampled and move to the next one. 105b3c9d1c33caf325aada244204215eb790c228c12dandov */ 106b3c9d1c33caf325aada244204215eb790c228c12dandov SkPoint next() { 107b3c9d1c33caf325aada244204215eb790c228c12dandov SkPoint point = fFwDiff[0]; 108b3c9d1c33caf325aada244204215eb790c228c12dandov fFwDiff[0] += fFwDiff[1]; 109b3c9d1c33caf325aada244204215eb790c228c12dandov fFwDiff[1] += fFwDiff[2]; 110b3c9d1c33caf325aada244204215eb790c228c12dandov fFwDiff[2] += fFwDiff[3]; 111b3c9d1c33caf325aada244204215eb790c228c12dandov fCurrent++; 112b3c9d1c33caf325aada244204215eb790c228c12dandov return point; 113b3c9d1c33caf325aada244204215eb790c228c12dandov } 1149d524f22bfde5dc3dc8f48e1be39bdebd3bb0304halcanary 115b3c9d1c33caf325aada244204215eb790c228c12dandov const SkPoint* getCtrlPoints() const { 116b3c9d1c33caf325aada244204215eb790c228c12dandov return fPoints; 117b3c9d1c33caf325aada244204215eb790c228c12dandov } 1189d524f22bfde5dc3dc8f48e1be39bdebd3bb0304halcanary 119b3c9d1c33caf325aada244204215eb790c228c12dandovprivate: 1205ba2b9612ae4bc3a244bf45f1ec55c3a5a41e181caryclark SkCubicCoeff fCoefs; 121b3c9d1c33caf325aada244204215eb790c228c12dandov int fMax, fCurrent, fDivisions; 1225ba2b9612ae4bc3a244bf45f1ec55c3a5a41e181caryclark SkPoint fFwDiff[4], fPoints[4]; 123b3c9d1c33caf325aada244204215eb790c228c12dandov}; 124b3c9d1c33caf325aada244204215eb790c228c12dandov 125b3c9d1c33caf325aada244204215eb790c228c12dandov//////////////////////////////////////////////////////////////////////////////// 126b3c9d1c33caf325aada244204215eb790c228c12dandov 127ecfff21bde1f0ca3c36533eded325066b5f2d42ddandov// size in pixels of each partition per axis, adjust this knob 128b3c9d1c33caf325aada244204215eb790c228c12dandovstatic const int kPartitionSize = 10; 129ecfff21bde1f0ca3c36533eded325066b5f2d42ddandov 130ecfff21bde1f0ca3c36533eded325066b5f2d42ddandov/** 131ecfff21bde1f0ca3c36533eded325066b5f2d42ddandov * Calculate the approximate arc length given a bezier curve's control points. 132ecfff21bde1f0ca3c36533eded325066b5f2d42ddandov */ 133ecfff21bde1f0ca3c36533eded325066b5f2d42ddandovstatic SkScalar approx_arc_length(SkPoint* points, int count) { 134ecfff21bde1f0ca3c36533eded325066b5f2d42ddandov if (count < 2) { 135ecfff21bde1f0ca3c36533eded325066b5f2d42ddandov return 0; 136ecfff21bde1f0ca3c36533eded325066b5f2d42ddandov } 137ecfff21bde1f0ca3c36533eded325066b5f2d42ddandov SkScalar arcLength = 0; 138ecfff21bde1f0ca3c36533eded325066b5f2d42ddandov for (int i = 0; i < count - 1; i++) { 139ecfff21bde1f0ca3c36533eded325066b5f2d42ddandov arcLength += SkPoint::Distance(points[i], points[i + 1]); 140ecfff21bde1f0ca3c36533eded325066b5f2d42ddandov } 141ecfff21bde1f0ca3c36533eded325066b5f2d42ddandov return arcLength; 142ecfff21bde1f0ca3c36533eded325066b5f2d42ddandov} 143ecfff21bde1f0ca3c36533eded325066b5f2d42ddandov 144b3c9d1c33caf325aada244204215eb790c228c12dandovstatic SkScalar bilerp(SkScalar tx, SkScalar ty, SkScalar c00, SkScalar c10, SkScalar c01, 145435071e8ab865e395bec34ef128a412ceb50f063Mike Reed SkScalar c11) { 146b3c9d1c33caf325aada244204215eb790c228c12dandov SkScalar a = c00 * (1.f - tx) + c10 * tx; 147b3c9d1c33caf325aada244204215eb790c228c12dandov SkScalar b = c01 * (1.f - tx) + c11 * tx; 148b3c9d1c33caf325aada244204215eb790c228c12dandov return a * (1.f - ty) + b * ty; 149b3c9d1c33caf325aada244204215eb790c228c12dandov} 150b3c9d1c33caf325aada244204215eb790c228c12dandov 151435071e8ab865e395bec34ef128a412ceb50f063Mike Reedstatic Sk4f bilerp(SkScalar tx, SkScalar ty, 152435071e8ab865e395bec34ef128a412ceb50f063Mike Reed const Sk4f& c00, const Sk4f& c10, const Sk4f& c01, const Sk4f& c11) { 153435071e8ab865e395bec34ef128a412ceb50f063Mike Reed Sk4f a = c00 * (1.f - tx) + c10 * tx; 154435071e8ab865e395bec34ef128a412ceb50f063Mike Reed Sk4f b = c01 * (1.f - tx) + c11 * tx; 155435071e8ab865e395bec34ef128a412ceb50f063Mike Reed return a * (1.f - ty) + b * ty; 156435071e8ab865e395bec34ef128a412ceb50f063Mike Reed} 157435071e8ab865e395bec34ef128a412ceb50f063Mike Reed 158b3c9d1c33caf325aada244204215eb790c228c12dandovSkISize SkPatchUtils::GetLevelOfDetail(const SkPoint cubics[12], const SkMatrix* matrix) { 1599d524f22bfde5dc3dc8f48e1be39bdebd3bb0304halcanary 160ecfff21bde1f0ca3c36533eded325066b5f2d42ddandov // Approximate length of each cubic. 161b3c9d1c33caf325aada244204215eb790c228c12dandov SkPoint pts[kNumPtsCubic]; 1624ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed SkPatchUtils::GetTopCubic(cubics, pts); 163b3c9d1c33caf325aada244204215eb790c228c12dandov matrix->mapPoints(pts, kNumPtsCubic); 164b3c9d1c33caf325aada244204215eb790c228c12dandov SkScalar topLength = approx_arc_length(pts, kNumPtsCubic); 1659d524f22bfde5dc3dc8f48e1be39bdebd3bb0304halcanary 1664ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed SkPatchUtils::GetBottomCubic(cubics, pts); 167b3c9d1c33caf325aada244204215eb790c228c12dandov matrix->mapPoints(pts, kNumPtsCubic); 168b3c9d1c33caf325aada244204215eb790c228c12dandov SkScalar bottomLength = approx_arc_length(pts, kNumPtsCubic); 1699d524f22bfde5dc3dc8f48e1be39bdebd3bb0304halcanary 1704ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed SkPatchUtils::GetLeftCubic(cubics, pts); 171b3c9d1c33caf325aada244204215eb790c228c12dandov matrix->mapPoints(pts, kNumPtsCubic); 172b3c9d1c33caf325aada244204215eb790c228c12dandov SkScalar leftLength = approx_arc_length(pts, kNumPtsCubic); 1739d524f22bfde5dc3dc8f48e1be39bdebd3bb0304halcanary 1744ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed SkPatchUtils::GetRightCubic(cubics, pts); 175b3c9d1c33caf325aada244204215eb790c228c12dandov matrix->mapPoints(pts, kNumPtsCubic); 176b3c9d1c33caf325aada244204215eb790c228c12dandov SkScalar rightLength = approx_arc_length(pts, kNumPtsCubic); 1779d524f22bfde5dc3dc8f48e1be39bdebd3bb0304halcanary 178ecfff21bde1f0ca3c36533eded325066b5f2d42ddandov // Level of detail per axis, based on the larger side between top and bottom or left and right 179ecfff21bde1f0ca3c36533eded325066b5f2d42ddandov int lodX = static_cast<int>(SkMaxScalar(topLength, bottomLength) / kPartitionSize); 180ecfff21bde1f0ca3c36533eded325066b5f2d42ddandov int lodY = static_cast<int>(SkMaxScalar(leftLength, rightLength) / kPartitionSize); 1819d524f22bfde5dc3dc8f48e1be39bdebd3bb0304halcanary 182b3c9d1c33caf325aada244204215eb790c228c12dandov return SkISize::Make(SkMax32(8, lodX), SkMax32(8, lodY)); 183b3c9d1c33caf325aada244204215eb790c228c12dandov} 184b3c9d1c33caf325aada244204215eb790c228c12dandov 1854ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reedvoid SkPatchUtils::GetTopCubic(const SkPoint cubics[12], SkPoint points[4]) { 186b3c9d1c33caf325aada244204215eb790c228c12dandov points[0] = cubics[kTopP0_CubicCtrlPts]; 187b3c9d1c33caf325aada244204215eb790c228c12dandov points[1] = cubics[kTopP1_CubicCtrlPts]; 188b3c9d1c33caf325aada244204215eb790c228c12dandov points[2] = cubics[kTopP2_CubicCtrlPts]; 189b3c9d1c33caf325aada244204215eb790c228c12dandov points[3] = cubics[kTopP3_CubicCtrlPts]; 190b3c9d1c33caf325aada244204215eb790c228c12dandov} 191b3c9d1c33caf325aada244204215eb790c228c12dandov 1924ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reedvoid SkPatchUtils::GetBottomCubic(const SkPoint cubics[12], SkPoint points[4]) { 193b3c9d1c33caf325aada244204215eb790c228c12dandov points[0] = cubics[kBottomP0_CubicCtrlPts]; 194b3c9d1c33caf325aada244204215eb790c228c12dandov points[1] = cubics[kBottomP1_CubicCtrlPts]; 195b3c9d1c33caf325aada244204215eb790c228c12dandov points[2] = cubics[kBottomP2_CubicCtrlPts]; 196b3c9d1c33caf325aada244204215eb790c228c12dandov points[3] = cubics[kBottomP3_CubicCtrlPts]; 197b3c9d1c33caf325aada244204215eb790c228c12dandov} 198b3c9d1c33caf325aada244204215eb790c228c12dandov 1994ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reedvoid SkPatchUtils::GetLeftCubic(const SkPoint cubics[12], SkPoint points[4]) { 200b3c9d1c33caf325aada244204215eb790c228c12dandov points[0] = cubics[kLeftP0_CubicCtrlPts]; 201b3c9d1c33caf325aada244204215eb790c228c12dandov points[1] = cubics[kLeftP1_CubicCtrlPts]; 202b3c9d1c33caf325aada244204215eb790c228c12dandov points[2] = cubics[kLeftP2_CubicCtrlPts]; 203b3c9d1c33caf325aada244204215eb790c228c12dandov points[3] = cubics[kLeftP3_CubicCtrlPts]; 204b3c9d1c33caf325aada244204215eb790c228c12dandov} 205b3c9d1c33caf325aada244204215eb790c228c12dandov 2064ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reedvoid SkPatchUtils::GetRightCubic(const SkPoint cubics[12], SkPoint points[4]) { 207b3c9d1c33caf325aada244204215eb790c228c12dandov points[0] = cubics[kRightP0_CubicCtrlPts]; 208b3c9d1c33caf325aada244204215eb790c228c12dandov points[1] = cubics[kRightP1_CubicCtrlPts]; 209b3c9d1c33caf325aada244204215eb790c228c12dandov points[2] = cubics[kRightP2_CubicCtrlPts]; 210b3c9d1c33caf325aada244204215eb790c228c12dandov points[3] = cubics[kRightP3_CubicCtrlPts]; 211b3c9d1c33caf325aada244204215eb790c228c12dandov} 212b3c9d1c33caf325aada244204215eb790c228c12dandov 213435071e8ab865e395bec34ef128a412ceb50f063Mike Reed#include "SkPM4fPriv.h" 214435071e8ab865e395bec34ef128a412ceb50f063Mike Reed#include "SkColorSpace_Base.h" 215435071e8ab865e395bec34ef128a412ceb50f063Mike Reed#include "SkColorSpaceXform.h" 216435071e8ab865e395bec34ef128a412ceb50f063Mike Reed 217435071e8ab865e395bec34ef128a412ceb50f063Mike Reedstruct SkRGBAf { 218435071e8ab865e395bec34ef128a412ceb50f063Mike Reed float fVec[4]; 219435071e8ab865e395bec34ef128a412ceb50f063Mike Reed 220435071e8ab865e395bec34ef128a412ceb50f063Mike Reed static SkRGBAf From4f(const Sk4f& x) { 221435071e8ab865e395bec34ef128a412ceb50f063Mike Reed SkRGBAf c; 222435071e8ab865e395bec34ef128a412ceb50f063Mike Reed x.store(c.fVec); 223435071e8ab865e395bec34ef128a412ceb50f063Mike Reed return c; 224435071e8ab865e395bec34ef128a412ceb50f063Mike Reed } 225435071e8ab865e395bec34ef128a412ceb50f063Mike Reed 226435071e8ab865e395bec34ef128a412ceb50f063Mike Reed static SkRGBAf FromBGRA32(SkColor c) { 227435071e8ab865e395bec34ef128a412ceb50f063Mike Reed return From4f(swizzle_rb(SkNx_cast<float>(Sk4b::Load(&c)) * (1/255.0f))); 228435071e8ab865e395bec34ef128a412ceb50f063Mike Reed } 229435071e8ab865e395bec34ef128a412ceb50f063Mike Reed 230435071e8ab865e395bec34ef128a412ceb50f063Mike Reed Sk4f to4f() const { 231435071e8ab865e395bec34ef128a412ceb50f063Mike Reed return Sk4f::Load(fVec); 232435071e8ab865e395bec34ef128a412ceb50f063Mike Reed } 233435071e8ab865e395bec34ef128a412ceb50f063Mike Reed 234435071e8ab865e395bec34ef128a412ceb50f063Mike Reed SkColor toBGRA32() const { 235435071e8ab865e395bec34ef128a412ceb50f063Mike Reed SkColor color; 236435071e8ab865e395bec34ef128a412ceb50f063Mike Reed SkNx_cast<uint8_t>(swizzle_rb(this->to4f()) * Sk4f(255) + Sk4f(0.5f)).store(&color); 237435071e8ab865e395bec34ef128a412ceb50f063Mike Reed return color; 238435071e8ab865e395bec34ef128a412ceb50f063Mike Reed } 239435071e8ab865e395bec34ef128a412ceb50f063Mike Reed 240435071e8ab865e395bec34ef128a412ceb50f063Mike Reed SkRGBAf premul() const { 241435071e8ab865e395bec34ef128a412ceb50f063Mike Reed float a = fVec[3]; 242435071e8ab865e395bec34ef128a412ceb50f063Mike Reed return From4f(this->to4f() * Sk4f(a, a, a, 1)); 243435071e8ab865e395bec34ef128a412ceb50f063Mike Reed } 244435071e8ab865e395bec34ef128a412ceb50f063Mike Reed 245435071e8ab865e395bec34ef128a412ceb50f063Mike Reed SkRGBAf unpremul() const { 246435071e8ab865e395bec34ef128a412ceb50f063Mike Reed float a = fVec[3]; 247435071e8ab865e395bec34ef128a412ceb50f063Mike Reed float inv = a ? 1/a : 0; 248435071e8ab865e395bec34ef128a412ceb50f063Mike Reed return From4f(this->to4f() * Sk4f(inv, inv, inv, 1)); 249435071e8ab865e395bec34ef128a412ceb50f063Mike Reed } 250435071e8ab865e395bec34ef128a412ceb50f063Mike Reed}; 251435071e8ab865e395bec34ef128a412ceb50f063Mike Reed 252435071e8ab865e395bec34ef128a412ceb50f063Mike Reedstatic void skcolor_to_linear(SkRGBAf dst[], const SkColor src[], int count, SkColorSpace* cs, 253435071e8ab865e395bec34ef128a412ceb50f063Mike Reed bool doPremul) { 254435071e8ab865e395bec34ef128a412ceb50f063Mike Reed if (cs) { 255435071e8ab865e395bec34ef128a412ceb50f063Mike Reed auto srcCS = SkColorSpace::MakeSRGB(); 256435071e8ab865e395bec34ef128a412ceb50f063Mike Reed auto dstCS = as_CSB(cs)->makeLinearGamma(); 257435071e8ab865e395bec34ef128a412ceb50f063Mike Reed auto op = doPremul ? SkColorSpaceXform::kPremul_AlphaOp 258435071e8ab865e395bec34ef128a412ceb50f063Mike Reed : SkColorSpaceXform::kPreserve_AlphaOp; 259435071e8ab865e395bec34ef128a412ceb50f063Mike Reed SkColorSpaceXform::Apply(dstCS.get(), SkColorSpaceXform::kRGBA_F32_ColorFormat, dst, 260435071e8ab865e395bec34ef128a412ceb50f063Mike Reed srcCS.get(), SkColorSpaceXform::kBGRA_8888_ColorFormat, src, 261435071e8ab865e395bec34ef128a412ceb50f063Mike Reed count, op); 262435071e8ab865e395bec34ef128a412ceb50f063Mike Reed } else { 263435071e8ab865e395bec34ef128a412ceb50f063Mike Reed for (int i = 0; i < count; ++i) { 264435071e8ab865e395bec34ef128a412ceb50f063Mike Reed dst[i] = SkRGBAf::FromBGRA32(src[i]); 265435071e8ab865e395bec34ef128a412ceb50f063Mike Reed if (doPremul) { 266435071e8ab865e395bec34ef128a412ceb50f063Mike Reed dst[i] = dst[i].premul(); 267435071e8ab865e395bec34ef128a412ceb50f063Mike Reed } 268435071e8ab865e395bec34ef128a412ceb50f063Mike Reed } 269435071e8ab865e395bec34ef128a412ceb50f063Mike Reed } 270435071e8ab865e395bec34ef128a412ceb50f063Mike Reed} 271435071e8ab865e395bec34ef128a412ceb50f063Mike Reed 272435071e8ab865e395bec34ef128a412ceb50f063Mike Reedstatic void linear_to_skcolor(SkColor dst[], const SkRGBAf src[], int count, SkColorSpace* cs) { 273435071e8ab865e395bec34ef128a412ceb50f063Mike Reed if (cs) { 274435071e8ab865e395bec34ef128a412ceb50f063Mike Reed auto srcCS = as_CSB(cs)->makeLinearGamma(); 275435071e8ab865e395bec34ef128a412ceb50f063Mike Reed auto dstCS = SkColorSpace::MakeSRGB(); 276435071e8ab865e395bec34ef128a412ceb50f063Mike Reed SkColorSpaceXform::Apply(dstCS.get(), SkColorSpaceXform::kBGRA_8888_ColorFormat, dst, 277435071e8ab865e395bec34ef128a412ceb50f063Mike Reed srcCS.get(), SkColorSpaceXform::kRGBA_F32_ColorFormat, src, 278435071e8ab865e395bec34ef128a412ceb50f063Mike Reed count, SkColorSpaceXform::kPreserve_AlphaOp); 279435071e8ab865e395bec34ef128a412ceb50f063Mike Reed } else { 280435071e8ab865e395bec34ef128a412ceb50f063Mike Reed for (int i = 0; i < count; ++i) { 281435071e8ab865e395bec34ef128a412ceb50f063Mike Reed dst[i] = src[i].toBGRA32(); 282435071e8ab865e395bec34ef128a412ceb50f063Mike Reed } 283435071e8ab865e395bec34ef128a412ceb50f063Mike Reed } 284435071e8ab865e395bec34ef128a412ceb50f063Mike Reed} 285435071e8ab865e395bec34ef128a412ceb50f063Mike Reed 286435071e8ab865e395bec34ef128a412ceb50f063Mike Reedstatic void unpremul(SkRGBAf array[], int count) { 287435071e8ab865e395bec34ef128a412ceb50f063Mike Reed for (int i = 0; i < count; ++i) { 288435071e8ab865e395bec34ef128a412ceb50f063Mike Reed array[i] = array[i].unpremul(); 289435071e8ab865e395bec34ef128a412ceb50f063Mike Reed } 290435071e8ab865e395bec34ef128a412ceb50f063Mike Reed} 291435071e8ab865e395bec34ef128a412ceb50f063Mike Reed 292795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reedsk_sp<SkVertices> SkPatchUtils::MakeVertices(const SkPoint cubics[12], const SkColor srcColors[4], 293435071e8ab865e395bec34ef128a412ceb50f063Mike Reed const SkPoint srcTexCoords[4], int lodX, int lodY, 294435071e8ab865e395bec34ef128a412ceb50f063Mike Reed bool interpColorsLinearly) { 295795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed if (lodX < 1 || lodY < 1 || nullptr == cubics) { 296795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed return nullptr; 297795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed } 298795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed 299795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed // check for overflow in multiplication 300795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed const int64_t lodX64 = (lodX + 1), 301795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed lodY64 = (lodY + 1), 302795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed mult64 = lodX64 * lodY64; 303795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed if (mult64 > SK_MaxS32) { 304795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed return nullptr; 305795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed } 306795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed 307795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed int vertexCount = SkToS32(mult64); 308795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed // it is recommended to generate draw calls of no more than 65536 indices, so we never generate 309795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed // more than 60000 indices. To accomplish that we resize the LOD and vertex count 310795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed if (vertexCount > 10000 || lodX > 200 || lodY > 200) { 311795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed float weightX = static_cast<float>(lodX) / (lodX + lodY); 312795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed float weightY = static_cast<float>(lodY) / (lodX + lodY); 313795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed 314795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed // 200 comes from the 100 * 2 which is the max value of vertices because of the limit of 315795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed // 60000 indices ( sqrt(60000 / 6) that comes from data->fIndexCount = lodX * lodY * 6) 316795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed lodX = static_cast<int>(weightX * 200); 317795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed lodY = static_cast<int>(weightY * 200); 318795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed vertexCount = (lodX + 1) * (lodY + 1); 319795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed } 320795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed const int indexCount = lodX * lodY * 6; 321795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed uint32_t flags = 0; 322795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed if (srcTexCoords) { 323795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed flags |= SkVertices::kHasTexCoords_BuilderFlag; 324795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed } 325795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed if (srcColors) { 326795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed flags |= SkVertices::kHasColors_BuilderFlag; 327795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed } 328795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed 32914a6430b7bcf92bcabf4aef18805969d1335aab1Florin Malita SkSTArenaAlloc<2048> alloc; 330435071e8ab865e395bec34ef128a412ceb50f063Mike Reed SkRGBAf* cornerColors = srcColors ? alloc.makeArray<SkRGBAf>(4) : nullptr; 331435071e8ab865e395bec34ef128a412ceb50f063Mike Reed SkRGBAf* tmpColors = srcColors ? alloc.makeArray<SkRGBAf>(vertexCount) : nullptr; 332435071e8ab865e395bec34ef128a412ceb50f063Mike Reed auto convertCS = interpColorsLinearly ? SkColorSpace::MakeSRGB() : nullptr; 333435071e8ab865e395bec34ef128a412ceb50f063Mike Reed 334887cdf112809727c51890ba8b98b3ddce22249f0Mike Reed SkVertices::Builder builder(SkVertices::kTriangles_VertexMode, vertexCount, indexCount, flags); 335795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed SkPoint* pos = builder.positions(); 336795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed SkPoint* texs = builder.texCoords(); 337795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed uint16_t* indices = builder.indices(); 3387346a1f3b74e10c6daf7973177ad7f7f575e9b97Mike Reed bool is_opaque = false; 339795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed 340435071e8ab865e395bec34ef128a412ceb50f063Mike Reed /* 341435071e8ab865e395bec34ef128a412ceb50f063Mike Reed * 1. Should we offer this as a runtime choice, as we do in gradients? 342435071e8ab865e395bec34ef128a412ceb50f063Mike Reed * 2. Since drawing the vertices wants premul, shoudl we extend SkVertices to store 343435071e8ab865e395bec34ef128a412ceb50f063Mike Reed * premul colors (as floats, w/ a colorspace)? 344435071e8ab865e395bec34ef128a412ceb50f063Mike Reed */ 345435071e8ab865e395bec34ef128a412ceb50f063Mike Reed bool doPremul = true; 346435071e8ab865e395bec34ef128a412ceb50f063Mike Reed if (cornerColors) { 3477346a1f3b74e10c6daf7973177ad7f7f575e9b97Mike Reed SkColor c = ~0; 348795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed for (int i = 0; i < kNumCorners; i++) { 3497346a1f3b74e10c6daf7973177ad7f7f575e9b97Mike Reed c &= srcColors[i]; 350795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed } 3517346a1f3b74e10c6daf7973177ad7f7f575e9b97Mike Reed is_opaque = (SkColorGetA(c) == 0xFF); 352435071e8ab865e395bec34ef128a412ceb50f063Mike Reed if (is_opaque) { 353435071e8ab865e395bec34ef128a412ceb50f063Mike Reed doPremul = false; // no need 354435071e8ab865e395bec34ef128a412ceb50f063Mike Reed } 355435071e8ab865e395bec34ef128a412ceb50f063Mike Reed 356435071e8ab865e395bec34ef128a412ceb50f063Mike Reed skcolor_to_linear(cornerColors, srcColors, kNumCorners, convertCS.get(), doPremul); 357795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed } 358795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed 359795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed SkPoint pts[kNumPtsCubic]; 3604ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed SkPatchUtils::GetBottomCubic(cubics, pts); 361795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed FwDCubicEvaluator fBottom(pts); 3624ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed SkPatchUtils::GetTopCubic(cubics, pts); 363795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed FwDCubicEvaluator fTop(pts); 3644ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed SkPatchUtils::GetLeftCubic(cubics, pts); 365795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed FwDCubicEvaluator fLeft(pts); 3664ebb43e94f39be5da8fc04fc0be8a63726bacffbMike Reed SkPatchUtils::GetRightCubic(cubics, pts); 367795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed FwDCubicEvaluator fRight(pts); 368795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed 369795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed fBottom.restart(lodX); 370795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed fTop.restart(lodX); 371795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed 372795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed SkScalar u = 0.0f; 373795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed int stride = lodY + 1; 374795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed for (int x = 0; x <= lodX; x++) { 375795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed SkPoint bottom = fBottom.next(), top = fTop.next(); 376795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed fLeft.restart(lodY); 377795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed fRight.restart(lodY); 378795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed SkScalar v = 0.f; 379795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed for (int y = 0; y <= lodY; y++) { 380795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed int dataIndex = x * (lodY + 1) + y; 381795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed 382795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed SkPoint left = fLeft.next(), right = fRight.next(); 383795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed 384795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed SkPoint s0 = SkPoint::Make((1.0f - v) * top.x() + v * bottom.x(), 385795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed (1.0f - v) * top.y() + v * bottom.y()); 386795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed SkPoint s1 = SkPoint::Make((1.0f - u) * left.x() + u * right.x(), 387795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed (1.0f - u) * left.y() + u * right.y()); 388795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed SkPoint s2 = SkPoint::Make( 389795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed (1.0f - v) * ((1.0f - u) * fTop.getCtrlPoints()[0].x() 390795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed + u * fTop.getCtrlPoints()[3].x()) 391795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed + v * ((1.0f - u) * fBottom.getCtrlPoints()[0].x() 392795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed + u * fBottom.getCtrlPoints()[3].x()), 393795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed (1.0f - v) * ((1.0f - u) * fTop.getCtrlPoints()[0].y() 394795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed + u * fTop.getCtrlPoints()[3].y()) 395795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed + v * ((1.0f - u) * fBottom.getCtrlPoints()[0].y() 396795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed + u * fBottom.getCtrlPoints()[3].y())); 397795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed pos[dataIndex] = s0 + s1 - s2; 398795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed 399435071e8ab865e395bec34ef128a412ceb50f063Mike Reed if (cornerColors) { 400435071e8ab865e395bec34ef128a412ceb50f063Mike Reed bilerp(u, v, cornerColors[kTopLeft_Corner].to4f(), 401435071e8ab865e395bec34ef128a412ceb50f063Mike Reed cornerColors[kTopRight_Corner].to4f(), 402435071e8ab865e395bec34ef128a412ceb50f063Mike Reed cornerColors[kBottomLeft_Corner].to4f(), 403435071e8ab865e395bec34ef128a412ceb50f063Mike Reed cornerColors[kBottomRight_Corner].to4f()).store(tmpColors[dataIndex].fVec); 404435071e8ab865e395bec34ef128a412ceb50f063Mike Reed if (is_opaque) { 405435071e8ab865e395bec34ef128a412ceb50f063Mike Reed tmpColors[dataIndex].fVec[3] = 1; 4067346a1f3b74e10c6daf7973177ad7f7f575e9b97Mike Reed } 407795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed } 408795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed 409795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed if (texs) { 410795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed texs[dataIndex] = SkPoint::Make(bilerp(u, v, srcTexCoords[kTopLeft_Corner].x(), 411795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed srcTexCoords[kTopRight_Corner].x(), 412795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed srcTexCoords[kBottomLeft_Corner].x(), 413795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed srcTexCoords[kBottomRight_Corner].x()), 414795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed bilerp(u, v, srcTexCoords[kTopLeft_Corner].y(), 415795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed srcTexCoords[kTopRight_Corner].y(), 416795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed srcTexCoords[kBottomLeft_Corner].y(), 417795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed srcTexCoords[kBottomRight_Corner].y())); 418795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed 419795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed } 420795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed 421795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed if(x < lodX && y < lodY) { 422795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed int i = 6 * (x * lodY + y); 423795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed indices[i] = x * stride + y; 424795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed indices[i + 1] = x * stride + 1 + y; 425795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed indices[i + 2] = (x + 1) * stride + 1 + y; 426795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed indices[i + 3] = indices[i]; 427795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed indices[i + 4] = indices[i + 2]; 428795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed indices[i + 5] = (x + 1) * stride + y; 429795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed } 430795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed v = SkScalarClampMax(v + 1.f / lodY, 1); 431795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed } 432795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed u = SkScalarClampMax(u + 1.f / lodX, 1); 433795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed } 434435071e8ab865e395bec34ef128a412ceb50f063Mike Reed 435435071e8ab865e395bec34ef128a412ceb50f063Mike Reed if (tmpColors) { 436435071e8ab865e395bec34ef128a412ceb50f063Mike Reed if (doPremul) { 437435071e8ab865e395bec34ef128a412ceb50f063Mike Reed unpremul(tmpColors, vertexCount); 438435071e8ab865e395bec34ef128a412ceb50f063Mike Reed } 439435071e8ab865e395bec34ef128a412ceb50f063Mike Reed linear_to_skcolor(builder.colors(), tmpColors, vertexCount, convertCS.get()); 440435071e8ab865e395bec34ef128a412ceb50f063Mike Reed } 441795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed return builder.detach(); 442795c5ea6572ca8b58961ed64d628cc407005f1b3Mike Reed} 443