| /* |
| * Copyright 2017 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "SkDrawShadowInfo.h" |
| #include "SkMatrix.h" |
| #include "SkPath.h" |
| #include "SkRect.h" |
| |
| namespace SkDrawShadowMetrics { |
| |
| static SkScalar compute_z(SkScalar x, SkScalar y, const SkPoint3& params) { |
| return x*params.fX + y*params.fY + params.fZ; |
| } |
| |
| void GetLocalBounds(const SkPath& path, const SkDrawShadowRec& rec, const SkMatrix& ctm, |
| SkRect* bounds) { |
| SkRect ambientBounds = path.getBounds(); |
| SkScalar occluderZ; |
| if (SkScalarNearlyZero(rec.fZPlaneParams.fX) && SkScalarNearlyZero(rec.fZPlaneParams.fY)) { |
| occluderZ = rec.fZPlaneParams.fZ; |
| } else { |
| occluderZ = compute_z(ambientBounds.fLeft, ambientBounds.fTop, rec.fZPlaneParams); |
| occluderZ = SkTMax(occluderZ, compute_z(ambientBounds.fRight, ambientBounds.fTop, |
| rec.fZPlaneParams)); |
| occluderZ = SkTMax(occluderZ, compute_z(ambientBounds.fLeft, ambientBounds.fBottom, |
| rec.fZPlaneParams)); |
| occluderZ = SkTMax(occluderZ, compute_z(ambientBounds.fRight, ambientBounds.fBottom, |
| rec.fZPlaneParams)); |
| } |
| SkScalar ambientBlur; |
| SkScalar spotBlur; |
| SkScalar spotScale; |
| SkPoint spotOffset; |
| if (ctm.hasPerspective()) { |
| // transform ambient and spot bounds into device space |
| ctm.mapRect(&ambientBounds); |
| |
| // get ambient blur (in device space) |
| ambientBlur = SkDrawShadowMetrics::AmbientBlurRadius(occluderZ); |
| |
| // get spot params (in device space) |
| SkPoint devLightPos = SkPoint::Make(rec.fLightPos.fX, rec.fLightPos.fY); |
| ctm.mapPoints(&devLightPos, 1); |
| SkDrawShadowMetrics::GetSpotParams(occluderZ, devLightPos.fX, devLightPos.fY, |
| rec.fLightPos.fZ, rec.fLightRadius, |
| &spotBlur, &spotScale, &spotOffset); |
| } else { |
| SkScalar devToSrcScale = SkScalarInvert(ctm.getMinScale()); |
| |
| // get ambient blur (in local space) |
| SkScalar devSpaceAmbientBlur = SkDrawShadowMetrics::AmbientBlurRadius(occluderZ); |
| ambientBlur = devSpaceAmbientBlur*devToSrcScale; |
| |
| // get spot params (in local space) |
| SkDrawShadowMetrics::GetSpotParams(occluderZ, rec.fLightPos.fX, rec.fLightPos.fY, |
| rec.fLightPos.fZ, rec.fLightRadius, |
| &spotBlur, &spotScale, &spotOffset); |
| |
| // convert spot blur to local space |
| spotBlur *= devToSrcScale; |
| } |
| |
| // in both cases, adjust ambient and spot bounds |
| SkRect spotBounds = ambientBounds; |
| ambientBounds.outset(ambientBlur, ambientBlur); |
| spotBounds.fLeft *= spotScale; |
| spotBounds.fTop *= spotScale; |
| spotBounds.fRight *= spotScale; |
| spotBounds.fBottom *= spotScale; |
| spotBounds.offset(spotOffset.fX, spotOffset.fY); |
| spotBounds.outset(spotBlur, spotBlur); |
| |
| // merge bounds |
| *bounds = ambientBounds; |
| bounds->join(spotBounds); |
| // outset a bit to account for floating point error |
| bounds->outset(1, 1); |
| |
| // if perspective, transform back to src space |
| if (ctm.hasPerspective()) { |
| // TODO: create tighter mapping from dev rect back to src rect |
| SkMatrix inverse; |
| if (ctm.invert(&inverse)) { |
| inverse.mapRect(bounds); |
| } |
| } |
| } |
| |
| |
| } |
| |