xref: /aosp_15_r20/external/skia/src/image/SkRescaleAndReadPixels.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2020 Google LLC
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "src/image/SkRescaleAndReadPixels.h"
9 
10 #include "include/core/SkBitmap.h"
11 #include "include/core/SkBlendMode.h"
12 #include "include/core/SkCanvas.h"
13 #include "include/core/SkColorSpace.h"
14 #include "include/core/SkColorType.h"
15 #include "include/core/SkImageInfo.h"
16 #include "include/core/SkPaint.h"
17 #include "include/core/SkPixmap.h"
18 #include "include/core/SkRect.h"
19 #include "include/core/SkRefCnt.h"
20 #include "include/core/SkSamplingOptions.h"
21 #include "include/core/SkSurface.h"
22 
23 #include <cmath>
24 #include <cstddef>
25 #include <memory>
26 #include <utility>
27 
SkRescaleAndReadPixels(SkBitmap bmp,const SkImageInfo & resultInfo,const SkIRect & srcRect,SkImage::RescaleGamma rescaleGamma,SkImage::RescaleMode rescaleMode,SkImage::ReadPixelsCallback callback,SkImage::ReadPixelsContext context)28 void SkRescaleAndReadPixels(SkBitmap bmp,
29                             const SkImageInfo& resultInfo,
30                             const SkIRect& srcRect,
31                             SkImage::RescaleGamma rescaleGamma,
32                             SkImage::RescaleMode rescaleMode,
33                             SkImage::ReadPixelsCallback callback,
34                             SkImage::ReadPixelsContext context) {
35     int srcW = srcRect.width();
36     int srcH = srcRect.height();
37 
38     float sx = (float)resultInfo.width() / srcW;
39     float sy = (float)resultInfo.height() / srcH;
40     // How many bilerp/bicubic steps to do in X and Y. + means upscaling, - means downscaling.
41     int stepsX;
42     int stepsY;
43     if (rescaleMode != SkImage::RescaleMode::kNearest) {
44         stepsX = static_cast<int>((sx > 1.f) ? std::ceil(std::log2f(sx))
45                                              : std::floor(std::log2f(sx)));
46         stepsY = static_cast<int>((sy > 1.f) ? std::ceil(std::log2f(sy))
47                                              : std::floor(std::log2f(sy)));
48     } else {
49         stepsX = sx != 1.f;
50         stepsY = sy != 1.f;
51     }
52 
53     SkPaint paint;
54     paint.setBlendMode(SkBlendMode::kSrc);
55     if (stepsX < 0 || stepsY < 0) {
56         // Don't trigger MIP generation. We don't currently have a way to trigger bicubic for
57         // downscaling draws.
58 
59         // TODO: should we trigger cubic now that we can?
60         if (rescaleMode != SkImage::RescaleMode::kNearest) {
61             rescaleMode = SkImage::RescaleMode::kRepeatedLinear;
62         }
63     }
64 
65     auto rescaling_to_sampling = [](SkImage::RescaleMode rescaleMode) {
66         SkSamplingOptions sampling;
67         if (rescaleMode == SkImage::RescaleMode::kRepeatedLinear) {
68             sampling = SkSamplingOptions(SkFilterMode::kLinear);
69         } else if (rescaleMode == SkImage::RescaleMode::kRepeatedCubic) {
70             sampling = SkSamplingOptions({1.0f/3, 1.0f/3});
71         }
72         return sampling;
73     };
74     SkSamplingOptions sampling = rescaling_to_sampling(rescaleMode);
75 
76     sk_sp<SkSurface> tempSurf;
77     sk_sp<SkImage> srcImage;
78     int srcX = srcRect.fLeft;
79     int srcY = srcRect.fTop;
80     SkCanvas::SrcRectConstraint constraint = SkCanvas::kStrict_SrcRectConstraint;
81     // Assume we should ignore the rescale linear request if the surface has no color space since
82     // it's unclear how we'd linearize from an unknown color space.
83     if (rescaleGamma == SkSurface::RescaleGamma::kLinear && bmp.info().colorSpace() &&
84         !bmp.info().colorSpace()->gammaIsLinear()) {
85         auto cs = bmp.info().colorSpace()->makeLinearGamma();
86         // Promote to F16 color type to preserve precision.
87         auto ii = SkImageInfo::Make(srcW, srcH, kRGBA_F16_SkColorType, bmp.info().alphaType(),
88                                     std::move(cs));
89         auto linearSurf = SkSurfaces::Raster(ii);
90         if (!linearSurf) {
91             callback(context, nullptr);
92             return;
93         }
94         linearSurf->getCanvas()->drawImage(bmp.asImage().get(), -srcX, -srcY, sampling, &paint);
95         tempSurf = std::move(linearSurf);
96         srcImage = tempSurf->makeImageSnapshot();
97         srcX = 0;
98         srcY = 0;
99         constraint = SkCanvas::kFast_SrcRectConstraint;
100     } else {
101         // MakeFromBitmap would trigger a copy if bmp is mutable.
102         srcImage = SkImages::RasterFromPixmap(bmp.pixmap(), nullptr, nullptr);
103     }
104     while (stepsX || stepsY) {
105         int nextW = resultInfo.width();
106         int nextH = resultInfo.height();
107         if (stepsX < 0) {
108             nextW = resultInfo.width() << (-stepsX - 1);
109             stepsX++;
110         } else if (stepsX != 0) {
111             if (stepsX > 1) {
112                 nextW = srcW * 2;
113             }
114             --stepsX;
115         }
116         if (stepsY < 0) {
117             nextH = resultInfo.height() << (-stepsY - 1);
118             stepsY++;
119         } else if (stepsY != 0) {
120             if (stepsY > 1) {
121                 nextH = srcH * 2;
122             }
123             --stepsY;
124         }
125         auto ii = srcImage->imageInfo().makeWH(nextW, nextH);
126         if (!stepsX && !stepsY) {
127             // Might as well fold conversion to final info in the last step.
128             ii = resultInfo;
129         }
130         auto next = SkSurfaces::Raster(ii);
131         if (!next) {
132             callback(context, nullptr);
133             return;
134         }
135         next->getCanvas()->drawImageRect(
136                 srcImage.get(), SkRect::Make(SkIRect::MakeXYWH(srcX, srcY, srcW, srcH)),
137                 SkRect::MakeIWH(nextW, nextH), sampling, &paint, constraint);
138         tempSurf = std::move(next);
139         srcImage = tempSurf->makeImageSnapshot();
140         srcX = srcY = 0;
141         srcW = nextW;
142         srcH = nextH;
143         constraint = SkCanvas::kFast_SrcRectConstraint;
144     }
145 
146     size_t rowBytes = resultInfo.minRowBytes();
147     std::unique_ptr<char[]> data(new char[resultInfo.height() * rowBytes]);
148     SkPixmap pm(resultInfo, data.get(), rowBytes);
149     if (srcImage->readPixels(nullptr, pm, srcX, srcY)) {
150         class Result : public SkImage::AsyncReadResult {
151         public:
152             Result(std::unique_ptr<const char[]> data, size_t rowBytes)
153                     : fData(std::move(data)), fRowBytes(rowBytes) {}
154             int count() const override { return 1; }
155             const void* data(int i) const override { return fData.get(); }
156             size_t rowBytes(int i) const override { return fRowBytes; }
157 
158         private:
159             std::unique_ptr<const char[]> fData;
160             size_t fRowBytes;
161         };
162         callback(context, std::make_unique<Result>(std::move(data), rowBytes));
163     } else {
164         callback(context, nullptr);
165     }
166 }
167