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