1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2017 Google Inc. 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci#include "include/core/SkMatrix.h" 9cb93a386Sopenharmony_ci#include "include/core/SkPath.h" 10cb93a386Sopenharmony_ci#include "include/core/SkRect.h" 11cb93a386Sopenharmony_ci#include "include/private/SkShadowFlags.h" 12cb93a386Sopenharmony_ci#include "src/core/SkDrawShadowInfo.h" 13cb93a386Sopenharmony_ci#include "src/utils/SkPolyUtils.h" 14cb93a386Sopenharmony_ci 15cb93a386Sopenharmony_civoid SkDrawShadowRec::dump(std::string& desc, int depth) const { 16cb93a386Sopenharmony_ci std::string split(depth, '\t'); 17cb93a386Sopenharmony_ci desc += split + "\n SkDrawShadowRec:{ \n"; 18cb93a386Sopenharmony_ci fZPlaneParams.dump(desc, depth + 1); 19cb93a386Sopenharmony_ci fLightPos.dump(desc, depth + 1); 20cb93a386Sopenharmony_ci desc += split + "\t fLightRadius: " + std::to_string(fLightRadius) + "\n"; 21cb93a386Sopenharmony_ci desc += split + "\t fAmbientColor: " + std::to_string(fAmbientColor) + "\n"; 22cb93a386Sopenharmony_ci desc += split + "\t fSpotColor: " + std::to_string(fSpotColor) + "\n"; 23cb93a386Sopenharmony_ci desc += split + "\t fFlags: " + std::to_string(fFlags) + "\n"; 24cb93a386Sopenharmony_ci desc += split + "}\n"; 25cb93a386Sopenharmony_ci} 26cb93a386Sopenharmony_ci 27cb93a386Sopenharmony_cinamespace SkDrawShadowMetrics { 28cb93a386Sopenharmony_ci 29cb93a386Sopenharmony_cistatic SkScalar compute_z(SkScalar x, SkScalar y, const SkPoint3& params) { 30cb93a386Sopenharmony_ci return x*params.fX + y*params.fY + params.fZ; 31cb93a386Sopenharmony_ci} 32cb93a386Sopenharmony_ci 33cb93a386Sopenharmony_cibool GetSpotShadowTransform(const SkPoint3& lightPos, SkScalar lightRadius, 34cb93a386Sopenharmony_ci const SkMatrix& ctm, const SkPoint3& zPlaneParams, 35cb93a386Sopenharmony_ci const SkRect& pathBounds, bool directional, 36cb93a386Sopenharmony_ci SkMatrix* shadowTransform, SkScalar* radius, bool isLimitElevation) { 37cb93a386Sopenharmony_ci auto heightFunc = [zPlaneParams] (SkScalar x, SkScalar y) { 38cb93a386Sopenharmony_ci return zPlaneParams.fX*x + zPlaneParams.fY*y + zPlaneParams.fZ; 39cb93a386Sopenharmony_ci }; 40cb93a386Sopenharmony_ci SkScalar occluderHeight = heightFunc(pathBounds.centerX(), pathBounds.centerY()); 41cb93a386Sopenharmony_ci 42cb93a386Sopenharmony_ci // TODO: have directional lights support tilt via the zPlaneParams 43cb93a386Sopenharmony_ci if (!ctm.hasPerspective() || directional) { 44cb93a386Sopenharmony_ci SkScalar scale; 45cb93a386Sopenharmony_ci SkVector translate; 46cb93a386Sopenharmony_ci if (directional) { 47cb93a386Sopenharmony_ci SkDrawShadowMetrics::GetDirectionalParams(occluderHeight, lightPos.fX, lightPos.fY, 48cb93a386Sopenharmony_ci lightPos.fZ, lightRadius, radius, 49cb93a386Sopenharmony_ci &scale, &translate); 50cb93a386Sopenharmony_ci } else { 51cb93a386Sopenharmony_ci SkDrawShadowMetrics::GetSpotParams(occluderHeight, lightPos.fX, lightPos.fY, 52cb93a386Sopenharmony_ci lightPos.fZ, lightRadius, radius, 53cb93a386Sopenharmony_ci &scale, &translate, isLimitElevation); 54cb93a386Sopenharmony_ci } 55cb93a386Sopenharmony_ci shadowTransform->setScaleTranslate(scale, scale, translate.fX, translate.fY); 56cb93a386Sopenharmony_ci shadowTransform->preConcat(ctm); 57cb93a386Sopenharmony_ci } else { 58cb93a386Sopenharmony_ci if (SkScalarNearlyZero(pathBounds.width()) || SkScalarNearlyZero(pathBounds.height())) { 59cb93a386Sopenharmony_ci return false; 60cb93a386Sopenharmony_ci } 61cb93a386Sopenharmony_ci 62cb93a386Sopenharmony_ci // get rotated quad in 3D 63cb93a386Sopenharmony_ci SkPoint pts[4]; 64cb93a386Sopenharmony_ci ctm.mapRectToQuad(pts, pathBounds); 65cb93a386Sopenharmony_ci // No shadows for bowties or other degenerate cases 66cb93a386Sopenharmony_ci if (!SkIsConvexPolygon(pts, 4)) { 67cb93a386Sopenharmony_ci return false; 68cb93a386Sopenharmony_ci } 69cb93a386Sopenharmony_ci SkPoint3 pts3D[4]; 70cb93a386Sopenharmony_ci SkScalar z = heightFunc(pathBounds.fLeft, pathBounds.fTop); 71cb93a386Sopenharmony_ci pts3D[0].set(pts[0].fX, pts[0].fY, z); 72cb93a386Sopenharmony_ci z = heightFunc(pathBounds.fRight, pathBounds.fTop); 73cb93a386Sopenharmony_ci pts3D[1].set(pts[1].fX, pts[1].fY, z); 74cb93a386Sopenharmony_ci z = heightFunc(pathBounds.fRight, pathBounds.fBottom); 75cb93a386Sopenharmony_ci pts3D[2].set(pts[2].fX, pts[2].fY, z); 76cb93a386Sopenharmony_ci z = heightFunc(pathBounds.fLeft, pathBounds.fBottom); 77cb93a386Sopenharmony_ci pts3D[3].set(pts[3].fX, pts[3].fY, z); 78cb93a386Sopenharmony_ci 79cb93a386Sopenharmony_ci // project from light through corners to z=0 plane 80cb93a386Sopenharmony_ci for (int i = 0; i < 4; ++i) { 81cb93a386Sopenharmony_ci SkScalar dz = lightPos.fZ - pts3D[i].fZ; 82cb93a386Sopenharmony_ci // light shouldn't be below or at a corner's z-location 83cb93a386Sopenharmony_ci if (dz <= SK_ScalarNearlyZero) { 84cb93a386Sopenharmony_ci return false; 85cb93a386Sopenharmony_ci } 86cb93a386Sopenharmony_ci SkScalar zRatio = pts3D[i].fZ / dz; 87cb93a386Sopenharmony_ci if (isLimitElevation) { 88cb93a386Sopenharmony_ci zRatio = 0.0f; 89cb93a386Sopenharmony_ci } 90cb93a386Sopenharmony_ci pts3D[i].fX -= (lightPos.fX - pts3D[i].fX)*zRatio; 91cb93a386Sopenharmony_ci pts3D[i].fY -= (lightPos.fY - pts3D[i].fY)*zRatio; 92cb93a386Sopenharmony_ci pts3D[i].fZ = SK_Scalar1; 93cb93a386Sopenharmony_ci } 94cb93a386Sopenharmony_ci 95cb93a386Sopenharmony_ci // Generate matrix that projects from [-1,1]x[-1,1] square to projected quad 96cb93a386Sopenharmony_ci SkPoint3 h0, h1, h2; 97cb93a386Sopenharmony_ci // Compute homogenous crossing point between top and bottom edges (gives new x-axis). 98cb93a386Sopenharmony_ci h0 = (pts3D[1].cross(pts3D[0])).cross(pts3D[2].cross(pts3D[3])); 99cb93a386Sopenharmony_ci // Compute homogenous crossing point between left and right edges (gives new y-axis). 100cb93a386Sopenharmony_ci h1 = (pts3D[0].cross(pts3D[3])).cross(pts3D[1].cross(pts3D[2])); 101cb93a386Sopenharmony_ci // Compute homogenous crossing point between diagonals (gives new origin). 102cb93a386Sopenharmony_ci h2 = (pts3D[0].cross(pts3D[2])).cross(pts3D[1].cross(pts3D[3])); 103cb93a386Sopenharmony_ci // If h2 is a vector (z=0 in 2D homogeneous space), that means that at least 104cb93a386Sopenharmony_ci // two of the quad corners are coincident and we don't have a realistic projection 105cb93a386Sopenharmony_ci if (SkScalarNearlyZero(h2.fZ)) { 106cb93a386Sopenharmony_ci return false; 107cb93a386Sopenharmony_ci } 108cb93a386Sopenharmony_ci // In some cases the crossing points are in the wrong direction 109cb93a386Sopenharmony_ci // to map (-1,-1) to pts3D[0], so we need to correct for that. 110cb93a386Sopenharmony_ci // Want h0 to be to the right of the left edge. 111cb93a386Sopenharmony_ci SkVector3 v = pts3D[3] - pts3D[0]; 112cb93a386Sopenharmony_ci SkVector3 w = h0 - pts3D[0]; 113cb93a386Sopenharmony_ci SkScalar perpDot = v.fX*w.fY - v.fY*w.fX; 114cb93a386Sopenharmony_ci if (perpDot > 0) { 115cb93a386Sopenharmony_ci h0 = -h0; 116cb93a386Sopenharmony_ci } 117cb93a386Sopenharmony_ci // Want h1 to be above the bottom edge. 118cb93a386Sopenharmony_ci v = pts3D[1] - pts3D[0]; 119cb93a386Sopenharmony_ci perpDot = v.fX*w.fY - v.fY*w.fX; 120cb93a386Sopenharmony_ci if (perpDot < 0) { 121cb93a386Sopenharmony_ci h1 = -h1; 122cb93a386Sopenharmony_ci } 123cb93a386Sopenharmony_ci shadowTransform->setAll(h0.fX / h2.fZ, h1.fX / h2.fZ, h2.fX / h2.fZ, 124cb93a386Sopenharmony_ci h0.fY / h2.fZ, h1.fY / h2.fZ, h2.fY / h2.fZ, 125cb93a386Sopenharmony_ci h0.fZ / h2.fZ, h1.fZ / h2.fZ, 1); 126cb93a386Sopenharmony_ci // generate matrix that transforms from bounds to [-1,1]x[-1,1] square 127cb93a386Sopenharmony_ci SkMatrix toHomogeneous; 128cb93a386Sopenharmony_ci SkScalar xScale = 2/(pathBounds.fRight - pathBounds.fLeft); 129cb93a386Sopenharmony_ci SkScalar yScale = 2/(pathBounds.fBottom - pathBounds.fTop); 130cb93a386Sopenharmony_ci toHomogeneous.setAll(xScale, 0, -xScale*pathBounds.fLeft - 1, 131cb93a386Sopenharmony_ci 0, yScale, -yScale*pathBounds.fTop - 1, 132cb93a386Sopenharmony_ci 0, 0, 1); 133cb93a386Sopenharmony_ci shadowTransform->preConcat(toHomogeneous); 134cb93a386Sopenharmony_ci 135cb93a386Sopenharmony_ci *radius = SkDrawShadowMetrics::SpotBlurRadius(occluderHeight, lightPos.fZ, lightRadius); 136cb93a386Sopenharmony_ci } 137cb93a386Sopenharmony_ci 138cb93a386Sopenharmony_ci return true; 139cb93a386Sopenharmony_ci} 140cb93a386Sopenharmony_ci 141cb93a386Sopenharmony_civoid GetLocalBounds(const SkPath& path, const SkDrawShadowRec& rec, const SkMatrix& ctm, 142cb93a386Sopenharmony_ci SkRect* bounds) { 143cb93a386Sopenharmony_ci SkRect ambientBounds = path.getBounds(); 144cb93a386Sopenharmony_ci SkScalar occluderZ; 145cb93a386Sopenharmony_ci if (SkScalarNearlyZero(rec.fZPlaneParams.fX) && SkScalarNearlyZero(rec.fZPlaneParams.fY)) { 146cb93a386Sopenharmony_ci occluderZ = rec.fZPlaneParams.fZ; 147cb93a386Sopenharmony_ci } else { 148cb93a386Sopenharmony_ci occluderZ = compute_z(ambientBounds.fLeft, ambientBounds.fTop, rec.fZPlaneParams); 149cb93a386Sopenharmony_ci occluderZ = std::max(occluderZ, compute_z(ambientBounds.fRight, ambientBounds.fTop, 150cb93a386Sopenharmony_ci rec.fZPlaneParams)); 151cb93a386Sopenharmony_ci occluderZ = std::max(occluderZ, compute_z(ambientBounds.fLeft, ambientBounds.fBottom, 152cb93a386Sopenharmony_ci rec.fZPlaneParams)); 153cb93a386Sopenharmony_ci occluderZ = std::max(occluderZ, compute_z(ambientBounds.fRight, ambientBounds.fBottom, 154cb93a386Sopenharmony_ci rec.fZPlaneParams)); 155cb93a386Sopenharmony_ci } 156cb93a386Sopenharmony_ci SkScalar ambientBlur; 157cb93a386Sopenharmony_ci SkScalar spotBlur; 158cb93a386Sopenharmony_ci SkScalar spotScale; 159cb93a386Sopenharmony_ci SkPoint spotOffset; 160cb93a386Sopenharmony_ci if (ctm.hasPerspective()) { 161cb93a386Sopenharmony_ci // transform ambient and spot bounds into device space 162cb93a386Sopenharmony_ci ctm.mapRect(&ambientBounds); 163cb93a386Sopenharmony_ci 164cb93a386Sopenharmony_ci // get ambient blur (in device space) 165cb93a386Sopenharmony_ci ambientBlur = SkDrawShadowMetrics::AmbientBlurRadius(occluderZ); 166cb93a386Sopenharmony_ci 167cb93a386Sopenharmony_ci // get spot params (in device space) 168cb93a386Sopenharmony_ci if (SkToBool(rec.fFlags & SkShadowFlags::kDirectionalLight_ShadowFlag)) { 169cb93a386Sopenharmony_ci SkDrawShadowMetrics::GetDirectionalParams(occluderZ, rec.fLightPos.fX, rec.fLightPos.fY, 170cb93a386Sopenharmony_ci rec.fLightPos.fZ, rec.fLightRadius, 171cb93a386Sopenharmony_ci &spotBlur, &spotScale, &spotOffset); 172cb93a386Sopenharmony_ci } else { 173cb93a386Sopenharmony_ci SkPoint devLightPos = SkPoint::Make(rec.fLightPos.fX, rec.fLightPos.fY); 174cb93a386Sopenharmony_ci ctm.mapPoints(&devLightPos, 1); 175cb93a386Sopenharmony_ci SkDrawShadowMetrics::GetSpotParams(occluderZ, devLightPos.fX, devLightPos.fY, 176cb93a386Sopenharmony_ci rec.fLightPos.fZ, rec.fLightRadius, 177cb93a386Sopenharmony_ci &spotBlur, &spotScale, &spotOffset, rec.isLimitElevation); 178cb93a386Sopenharmony_ci } 179cb93a386Sopenharmony_ci } else { 180cb93a386Sopenharmony_ci SkScalar devToSrcScale = SkScalarInvert(ctm.getMinScale()); 181cb93a386Sopenharmony_ci 182cb93a386Sopenharmony_ci // get ambient blur (in local space) 183cb93a386Sopenharmony_ci SkScalar devSpaceAmbientBlur = SkDrawShadowMetrics::AmbientBlurRadius(occluderZ); 184cb93a386Sopenharmony_ci ambientBlur = devSpaceAmbientBlur*devToSrcScale; 185cb93a386Sopenharmony_ci 186cb93a386Sopenharmony_ci // get spot params (in local space) 187cb93a386Sopenharmony_ci if (SkToBool(rec.fFlags & SkShadowFlags::kDirectionalLight_ShadowFlag)) { 188cb93a386Sopenharmony_ci SkDrawShadowMetrics::GetDirectionalParams(occluderZ, rec.fLightPos.fX, rec.fLightPos.fY, 189cb93a386Sopenharmony_ci rec.fLightPos.fZ, rec.fLightRadius, 190cb93a386Sopenharmony_ci &spotBlur, &spotScale, &spotOffset); 191cb93a386Sopenharmony_ci // light dir is in device space, so need to map spot offset back into local space 192cb93a386Sopenharmony_ci SkMatrix inverse; 193cb93a386Sopenharmony_ci if (ctm.invert(&inverse)) { 194cb93a386Sopenharmony_ci inverse.mapVectors(&spotOffset, 1); 195cb93a386Sopenharmony_ci } 196cb93a386Sopenharmony_ci } else { 197cb93a386Sopenharmony_ci SkDrawShadowMetrics::GetSpotParams(occluderZ, rec.fLightPos.fX, rec.fLightPos.fY, 198cb93a386Sopenharmony_ci rec.fLightPos.fZ, rec.fLightRadius, 199cb93a386Sopenharmony_ci &spotBlur, &spotScale, &spotOffset, rec.isLimitElevation); 200cb93a386Sopenharmony_ci } 201cb93a386Sopenharmony_ci 202cb93a386Sopenharmony_ci // convert spot blur to local space 203cb93a386Sopenharmony_ci spotBlur *= devToSrcScale; 204cb93a386Sopenharmony_ci } 205cb93a386Sopenharmony_ci 206cb93a386Sopenharmony_ci // in both cases, adjust ambient and spot bounds 207cb93a386Sopenharmony_ci SkRect spotBounds = ambientBounds; 208cb93a386Sopenharmony_ci ambientBounds.outset(ambientBlur, ambientBlur); 209cb93a386Sopenharmony_ci spotBounds.fLeft *= spotScale; 210cb93a386Sopenharmony_ci spotBounds.fTop *= spotScale; 211cb93a386Sopenharmony_ci spotBounds.fRight *= spotScale; 212cb93a386Sopenharmony_ci spotBounds.fBottom *= spotScale; 213cb93a386Sopenharmony_ci spotBounds.offset(spotOffset.fX, spotOffset.fY); 214cb93a386Sopenharmony_ci spotBounds.outset(spotBlur, spotBlur); 215cb93a386Sopenharmony_ci 216cb93a386Sopenharmony_ci // merge bounds 217cb93a386Sopenharmony_ci *bounds = ambientBounds; 218cb93a386Sopenharmony_ci bounds->join(spotBounds); 219cb93a386Sopenharmony_ci // outset a bit to account for floating point error 220cb93a386Sopenharmony_ci bounds->outset(1, 1); 221cb93a386Sopenharmony_ci 222cb93a386Sopenharmony_ci // if perspective, transform back to src space 223cb93a386Sopenharmony_ci if (ctm.hasPerspective()) { 224cb93a386Sopenharmony_ci // TODO: create tighter mapping from dev rect back to src rect 225cb93a386Sopenharmony_ci SkMatrix inverse; 226cb93a386Sopenharmony_ci if (ctm.invert(&inverse)) { 227cb93a386Sopenharmony_ci inverse.mapRect(bounds); 228cb93a386Sopenharmony_ci } 229cb93a386Sopenharmony_ci } 230cb93a386Sopenharmony_ci} 231cb93a386Sopenharmony_ci 232cb93a386Sopenharmony_ci 233cb93a386Sopenharmony_ci} // namespace SkDrawShadowMetrics 234cb93a386Sopenharmony_ci 235