• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "LayerDrawable.h"
18 
19 #include <shaders/shaders.h>
20 #include <utils/Color.h>
21 #include <utils/MathUtils.h>
22 
23 #include "DeviceInfo.h"
24 #include "GrBackendSurface.h"
25 #include "SkColorFilter.h"
26 #include "SkRuntimeEffect.h"
27 #include "SkSurface.h"
28 #include "gl/GrGLTypes.h"
29 #include "math/mat4.h"
30 #include "system/graphics-base-v1.0.h"
31 #include "system/window.h"
32 
33 namespace android {
34 namespace uirenderer {
35 namespace skiapipeline {
36 
onDraw(SkCanvas * canvas)37 void LayerDrawable::onDraw(SkCanvas* canvas) {
38     Layer* layer = mLayerUpdater->backingLayer();
39     if (layer) {
40         SkRect srcRect = layer->getCurrentCropRect();
41         DrawLayer(canvas->recordingContext(), canvas, layer, &srcRect, nullptr, true);
42     }
43 }
44 
isIntegerAligned(SkScalar x)45 static inline SkScalar isIntegerAligned(SkScalar x) {
46     return MathUtils::isZero(roundf(x) - x);
47 }
48 
49 // Disable filtering when there is no scaling in screen coordinates and the corners have the same
50 // fraction (for translate) or zero fraction (for any other rect-to-rect transform).
shouldFilterRect(const SkMatrix & matrix,const SkRect & srcRect,const SkRect & dstRect)51 static bool shouldFilterRect(const SkMatrix& matrix, const SkRect& srcRect, const SkRect& dstRect) {
52     if (!matrix.rectStaysRect()) return true;
53     SkRect dstDevRect = matrix.mapRect(dstRect);
54     float dstW, dstH;
55     if (MathUtils::isZero(matrix.getScaleX()) && MathUtils::isZero(matrix.getScaleY())) {
56         // Has a 90 or 270 degree rotation, although total matrix may also have scale factors
57         // in m10 and m01. Those scalings are automatically handled by mapRect so comparing
58         // dimensions is sufficient, but swap width and height comparison.
59         dstW = dstDevRect.height();
60         dstH = dstDevRect.width();
61     } else {
62         // Handle H/V flips or 180 rotation matrices. Axes may have been mirrored, but
63         // dimensions are still safe to compare directly.
64         dstW = dstDevRect.width();
65         dstH = dstDevRect.height();
66     }
67     if (!(MathUtils::areEqual(dstW, srcRect.width()) &&
68           MathUtils::areEqual(dstH, srcRect.height()))) {
69         return true;
70     }
71     // Device rect and source rect should be integer aligned to ensure there's no difference
72     // in how nearest-neighbor sampling is resolved.
73     return !(isIntegerAligned(srcRect.x()) &&
74              isIntegerAligned(srcRect.y()) &&
75              isIntegerAligned(dstDevRect.x()) &&
76              isIntegerAligned(dstDevRect.y()));
77 }
78 
createLinearEffectShader(sk_sp<SkShader> shader,const shaders::LinearEffect & linearEffect,float maxDisplayLuminance,float currentDisplayLuminanceNits,float maxLuminance)79 static sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> shader,
80                                                 const shaders::LinearEffect& linearEffect,
81                                                 float maxDisplayLuminance,
82                                                 float currentDisplayLuminanceNits,
83                                                 float maxLuminance) {
84     auto shaderString = SkString(shaders::buildLinearEffectSkSL(linearEffect));
85     auto [runtimeEffect, error] = SkRuntimeEffect::MakeForShader(std::move(shaderString));
86     if (!runtimeEffect) {
87         LOG_ALWAYS_FATAL("LinearColorFilter construction error: %s", error.c_str());
88     }
89 
90     SkRuntimeShaderBuilder effectBuilder(std::move(runtimeEffect));
91 
92     effectBuilder.child("child") = std::move(shader);
93 
94     const auto uniforms = shaders::buildLinearEffectUniforms(
95             linearEffect, mat4(), maxDisplayLuminance, currentDisplayLuminanceNits, maxLuminance);
96 
97     for (const auto& uniform : uniforms) {
98         effectBuilder.uniform(uniform.name.c_str()).set(uniform.value.data(), uniform.value.size());
99     }
100 
101     return effectBuilder.makeShader();
102 }
103 
isHdrDataspace(ui::Dataspace dataspace)104 static bool isHdrDataspace(ui::Dataspace dataspace) {
105     const auto transfer = dataspace & HAL_DATASPACE_TRANSFER_MASK;
106 
107     return transfer == HAL_DATASPACE_TRANSFER_ST2084 || transfer == HAL_DATASPACE_TRANSFER_HLG;
108 }
109 
110 // TODO: Context arg probably doesn't belong here – do debug check at callsite instead.
DrawLayer(GrRecordingContext * context,SkCanvas * canvas,Layer * layer,const SkRect * srcRect,const SkRect * dstRect,bool useLayerTransform)111 bool LayerDrawable::DrawLayer(GrRecordingContext* context,
112                               SkCanvas* canvas,
113                               Layer* layer,
114                               const SkRect* srcRect,
115                               const SkRect* dstRect,
116                               bool useLayerTransform) {
117     if (context == nullptr) {
118         ALOGD("Attempting to draw LayerDrawable into an unsupported surface");
119         return false;
120     }
121     // transform the matrix based on the layer
122     // SkMatrix layerTransform = layer->getTransform();
123     const uint32_t windowTransform = layer->getWindowTransform();
124     sk_sp<SkImage> layerImage = layer->getImage();
125     const int layerWidth = layer->getWidth();
126     const int layerHeight = layer->getHeight();
127 
128     if (layerImage) {
129         const int imageWidth = layerImage->width();
130         const int imageHeight = layerImage->height();
131 
132         if (useLayerTransform) {
133             canvas->save();
134             canvas->concat(layer->getTransform());
135         }
136 
137         SkPaint paint;
138         paint.setAlpha(layer->getAlpha());
139         paint.setBlendMode(layer->getMode());
140         paint.setColorFilter(layer->getColorFilter());
141         const SkMatrix& totalMatrix = canvas->getTotalMatrix();
142         SkRect skiaSrcRect;
143         if (srcRect && !srcRect->isEmpty()) {
144             skiaSrcRect = *srcRect;
145         } else {
146             skiaSrcRect = SkRect::MakeIWH(imageWidth, imageHeight);
147         }
148         SkRect skiaDestRect;
149         if (dstRect && !dstRect->isEmpty()) {
150             skiaDestRect = (windowTransform & NATIVE_WINDOW_TRANSFORM_ROT_90)
151                                    ? SkRect::MakeIWH(dstRect->height(), dstRect->width())
152                                    : SkRect::MakeIWH(dstRect->width(), dstRect->height());
153         } else {
154             skiaDestRect = (windowTransform & NATIVE_WINDOW_TRANSFORM_ROT_90)
155                                    ? SkRect::MakeIWH(layerHeight, layerWidth)
156                                    : SkRect::MakeIWH(layerWidth, layerHeight);
157         }
158 
159         const float px = skiaDestRect.centerX();
160         const float py = skiaDestRect.centerY();
161         SkMatrix m;
162         if (windowTransform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
163             m.postScale(-1.f, 1.f, px, py);
164         }
165         if (windowTransform & NATIVE_WINDOW_TRANSFORM_FLIP_V) {
166             m.postScale(1.f, -1.f, px, py);
167         }
168         if (windowTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
169             m.postRotate(90, 0, 0);
170             m.postTranslate(skiaDestRect.height(), 0);
171         }
172         auto constraint = SkCanvas::kFast_SrcRectConstraint;
173         if (srcRect && !srcRect->isEmpty()) {
174             constraint = SkCanvas::kStrict_SrcRectConstraint;
175         }
176 
177         canvas->save();
178         canvas->concat(m);
179 
180         // If (matrix is a rect-to-rect transform)
181         // and (src/dst buffers size match in screen coordinates)
182         // and (src/dst corners align fractionally),
183         // then use nearest neighbor, otherwise use bilerp sampling.
184         // Skia TextureOp has the above logic build-in, but not NonAAFillRectOp. TextureOp works
185         // only for SrcOver blending and without color filter (readback uses Src blending).
186         SkSamplingOptions sampling(SkFilterMode::kNearest);
187         if (layer->getForceFilter() || shouldFilterRect(totalMatrix, skiaSrcRect, skiaDestRect)) {
188             sampling = SkSamplingOptions(SkFilterMode::kLinear);
189         }
190 
191         const auto sourceDataspace = static_cast<ui::Dataspace>(
192                 ColorSpaceToADataSpace(layerImage->colorSpace(), layerImage->colorType()));
193         const SkImageInfo& imageInfo = canvas->imageInfo();
194         const auto destinationDataspace = static_cast<ui::Dataspace>(
195                 ColorSpaceToADataSpace(imageInfo.colorSpace(), imageInfo.colorType()));
196 
197         if (isHdrDataspace(sourceDataspace) || isHdrDataspace(destinationDataspace)) {
198             const auto effect = shaders::LinearEffect{
199                     .inputDataspace = sourceDataspace,
200                     .outputDataspace = destinationDataspace,
201                     .undoPremultipliedAlpha = layerImage->alphaType() == kPremul_SkAlphaType,
202                     .fakeInputDataspace = destinationDataspace};
203             auto shader = layerImage->makeShader(sampling,
204                                                  SkMatrix::RectToRect(skiaSrcRect, skiaDestRect));
205             constexpr float kMaxDisplayBrightess = 1000.f;
206             constexpr float kCurrentDisplayBrightness = 500.f;
207             shader = createLinearEffectShader(std::move(shader), effect, kMaxDisplayBrightess,
208                                               kCurrentDisplayBrightness,
209                                               layer->getMaxLuminanceNits());
210             paint.setShader(shader);
211             canvas->drawRect(skiaDestRect, paint);
212         } else {
213             canvas->drawImageRect(layerImage.get(), skiaSrcRect, skiaDestRect, sampling, &paint,
214                                   constraint);
215         }
216 
217         canvas->restore();
218         // restore the original matrix
219         if (useLayerTransform) {
220             canvas->restore();
221         }
222     }
223 
224     return layerImage != nullptr;
225 }
226 
227 }  // namespace skiapipeline
228 }  // namespace uirenderer
229 }  // namespace android
230