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 "Tonemapper.h"
29 #include "gl/GrGLTypes.h"
30 #include "math/mat4.h"
31 #include "system/graphics-base-v1.0.h"
32 #include "system/window.h"
33 
34 namespace android {
35 namespace uirenderer {
36 namespace skiapipeline {
37 
onDraw(SkCanvas * canvas)38 void LayerDrawable::onDraw(SkCanvas* canvas) {
39     Layer* layer = mLayerUpdater->backingLayer();
40     if (layer) {
41         SkRect srcRect = layer->getCurrentCropRect();
42         DrawLayer(canvas->recordingContext(), canvas, layer, &srcRect, nullptr, true);
43     }
44 }
45 
isIntegerAligned(SkScalar x)46 static inline SkScalar isIntegerAligned(SkScalar x) {
47     return MathUtils::isZero(roundf(x) - x);
48 }
49 
50 // Disable filtering when there is no scaling in screen coordinates and the corners have the same
51 // fraction (for translate) or zero fraction (for any other rect-to-rect transform).
shouldFilterRect(const SkMatrix & matrix,const SkRect & srcRect,const SkRect & dstRect)52 static bool shouldFilterRect(const SkMatrix& matrix, const SkRect& srcRect, const SkRect& dstRect) {
53     if (!matrix.rectStaysRect()) return true;
54     SkRect dstDevRect = matrix.mapRect(dstRect);
55     float dstW, dstH;
56     if (MathUtils::isZero(matrix.getScaleX()) && MathUtils::isZero(matrix.getScaleY())) {
57         // Has a 90 or 270 degree rotation, although total matrix may also have scale factors
58         // in m10 and m01. Those scalings are automatically handled by mapRect so comparing
59         // dimensions is sufficient, but swap width and height comparison.
60         dstW = dstDevRect.height();
61         dstH = dstDevRect.width();
62     } else {
63         // Handle H/V flips or 180 rotation matrices. Axes may have been mirrored, but
64         // dimensions are still safe to compare directly.
65         dstW = dstDevRect.width();
66         dstH = dstDevRect.height();
67     }
68     if (!(MathUtils::areEqual(dstW, srcRect.width()) &&
69           MathUtils::areEqual(dstH, srcRect.height()))) {
70         return true;
71     }
72     // Device rect and source rect should be integer aligned to ensure there's no difference
73     // in how nearest-neighbor sampling is resolved.
74     return !(isIntegerAligned(srcRect.x()) &&
75              isIntegerAligned(srcRect.y()) &&
76              isIntegerAligned(dstDevRect.x()) &&
77              isIntegerAligned(dstDevRect.y()));
78 }
79 
adjustCropForYUV(uint32_t format,int bufferWidth,int bufferHeight,SkRect * cropRect)80 static void adjustCropForYUV(uint32_t format, int bufferWidth, int bufferHeight, SkRect* cropRect) {
81     // Chroma channels of YUV420 images are subsampled we may need to shrink the crop region by
82     // a whole texel on each side. Since skia still adds its own 0.5 inset, we apply an
83     // additional 0.5 inset. See GLConsumer::computeTransformMatrix for details.
84     float shrinkAmount = 0.0f;
85     switch (format) {
86         // Use HAL formats since some AHB formats are only available in vndk
87         case HAL_PIXEL_FORMAT_YCBCR_420_888:
88         case HAL_PIXEL_FORMAT_YV12:
89         case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
90             shrinkAmount = 0.5f;
91             break;
92         default:
93             break;
94     }
95 
96     // Shrink the crop if it has more than 1-px and differs from the buffer size.
97     if (cropRect->width() > 1 && cropRect->width() < bufferWidth) {
98         cropRect->inset(shrinkAmount, 0);
99     }
100 
101     if (cropRect->height() > 1 && cropRect->height() < bufferHeight) {
102         cropRect->inset(0, shrinkAmount);
103     }
104 }
105 
106 // 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)107 bool LayerDrawable::DrawLayer(GrRecordingContext* context,
108                               SkCanvas* canvas,
109                               Layer* layer,
110                               const SkRect* srcRect,
111                               const SkRect* dstRect,
112                               bool useLayerTransform) {
113     if (context == nullptr) {
114         ALOGD("Attempting to draw LayerDrawable into an unsupported surface");
115         return false;
116     }
117     // transform the matrix based on the layer
118     // SkMatrix layerTransform = layer->getTransform();
119     const uint32_t windowTransform = layer->getWindowTransform();
120     sk_sp<SkImage> layerImage = layer->getImage();
121     const int layerWidth = layer->getWidth();
122     const int layerHeight = layer->getHeight();
123 
124     if (layerImage) {
125         const int imageWidth = layerImage->width();
126         const int imageHeight = layerImage->height();
127 
128         if (useLayerTransform) {
129             canvas->save();
130             canvas->concat(layer->getTransform());
131         }
132 
133         SkPaint paint;
134         paint.setAlpha(layer->getAlpha());
135         paint.setBlendMode(layer->getMode());
136         paint.setColorFilter(layer->getColorFilter());
137         const SkMatrix& totalMatrix = canvas->getTotalMatrix();
138         SkRect skiaSrcRect;
139         if (srcRect && !srcRect->isEmpty()) {
140             skiaSrcRect = *srcRect;
141             adjustCropForYUV(layer->getBufferFormat(), imageWidth, imageHeight, &skiaSrcRect);
142         } else {
143             skiaSrcRect = SkRect::MakeIWH(imageWidth, imageHeight);
144         }
145         SkRect skiaDestRect;
146         if (dstRect && !dstRect->isEmpty()) {
147             skiaDestRect = (windowTransform & NATIVE_WINDOW_TRANSFORM_ROT_90)
148                                    ? SkRect::MakeIWH(dstRect->height(), dstRect->width())
149                                    : SkRect::MakeIWH(dstRect->width(), dstRect->height());
150         } else {
151             skiaDestRect = (windowTransform & NATIVE_WINDOW_TRANSFORM_ROT_90)
152                                    ? SkRect::MakeIWH(layerHeight, layerWidth)
153                                    : SkRect::MakeIWH(layerWidth, layerHeight);
154         }
155 
156         const float px = skiaDestRect.centerX();
157         const float py = skiaDestRect.centerY();
158         SkMatrix m;
159         if (windowTransform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
160             m.postScale(-1.f, 1.f, px, py);
161         }
162         if (windowTransform & NATIVE_WINDOW_TRANSFORM_FLIP_V) {
163             m.postScale(1.f, -1.f, px, py);
164         }
165         if (windowTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
166             m.postRotate(90, 0, 0);
167             m.postTranslate(skiaDestRect.height(), 0);
168         }
169         auto constraint = SkCanvas::kFast_SrcRectConstraint;
170         if (srcRect && !srcRect->isEmpty()) {
171             constraint = SkCanvas::kStrict_SrcRectConstraint;
172         }
173 
174         canvas->save();
175         canvas->concat(m);
176 
177         // If (matrix is a rect-to-rect transform)
178         // and (src/dst buffers size match in screen coordinates)
179         // and (src/dst corners align fractionally),
180         // then use nearest neighbor, otherwise use bilerp sampling.
181         // Skia TextureOp has the above logic build-in, but not NonAAFillRectOp. TextureOp works
182         // only for SrcOver blending and without color filter (readback uses Src blending).
183         SkSamplingOptions sampling(SkFilterMode::kNearest);
184         if (layer->getForceFilter() || shouldFilterRect(totalMatrix, skiaSrcRect, skiaDestRect)) {
185             sampling = SkSamplingOptions(SkFilterMode::kLinear);
186         }
187 
188         tonemapPaint(layerImage->imageInfo(), canvas->imageInfo(), layer->getMaxLuminanceNits(),
189                      paint);
190         canvas->drawImageRect(layerImage.get(), skiaSrcRect, skiaDestRect, sampling, &paint,
191                               constraint);
192 
193         canvas->restore();
194         // restore the original matrix
195         if (useLayerTransform) {
196             canvas->restore();
197         }
198     }
199 
200     return layerImage != nullptr;
201 }
202 
203 }  // namespace skiapipeline
204 }  // namespace uirenderer
205 }  // namespace android
206