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