2 * Copyright 2020 Google LLC
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "include/core/SkBitmap.h"
9 #include "include/core/SkCanvas.h"
10 #include "include/core/SkColorSpace.h"
11 #include "include/core/SkImageInfo.h"
12 #include "include/core/SkPaint.h"
13 #include "include/core/SkRect.h"
14 #include "include/core/SkSurface.h"
18 void SkRescaleAndReadPixels(SkBitmap bmp,
19 const SkImageInfo& resultInfo,
20 const SkIRect& srcRect,
21 SkImage::RescaleGamma rescaleGamma,
22 SkImage::RescaleMode rescaleMode,
23 SkImage::ReadPixelsCallback callback,
24 SkImage::ReadPixelsContext context) {
25 int srcW = srcRect.width();
26 int srcH = srcRect.height();
28 float sx = (float)resultInfo.width() / srcW;
29 float sy = (float)resultInfo.height() / srcH;
30 // How many bilerp/bicubic steps to do in X and Y. + means upscaling, - means downscaling.
33 if (rescaleMode != SkImage::RescaleMode::kNearest) {
34 stepsX = static_cast<int>((sx > 1.f) ? std::ceil(std::log2f(sx))
35 : std::floor(std::log2f(sx)));
36 stepsY = static_cast<int>((sy > 1.f) ? std::ceil(std::log2f(sy))
37 : std::floor(std::log2f(sy)));
44 paint.setBlendMode(SkBlendMode::kSrc);
45 if (stepsX < 0 || stepsY < 0) {
46 // Don't trigger MIP generation. We don't currently have a way to trigger bicubic for
49 // TODO: should we trigger cubic now that we can?
50 if (rescaleMode != SkImage::RescaleMode::kNearest) {
51 rescaleMode = SkImage::RescaleMode::kRepeatedLinear;
55 auto rescaling_to_sampling = [](SkImage::RescaleMode rescaleMode) {
56 SkSamplingOptions sampling;
57 if (rescaleMode == SkImage::RescaleMode::kRepeatedLinear) {
58 sampling = SkSamplingOptions(SkFilterMode::kLinear);
59 } else if (rescaleMode == SkImage::RescaleMode::kRepeatedCubic) {
60 sampling = SkSamplingOptions({1.0f/3, 1.0f/3});
64 SkSamplingOptions sampling = rescaling_to_sampling(rescaleMode);
66 sk_sp<SkSurface> tempSurf;
67 sk_sp<SkImage> srcImage;
68 int srcX = srcRect.fLeft;
69 int srcY = srcRect.fTop;
70 SkCanvas::SrcRectConstraint constraint = SkCanvas::kStrict_SrcRectConstraint;
71 // Assume we should ignore the rescale linear request if the surface has no color space since
72 // it's unclear how we'd linearize from an unknown color space.
73 if (rescaleGamma == SkSurface::RescaleGamma::kLinear && bmp.info().colorSpace() &&
74 !bmp.info().colorSpace()->gammaIsLinear()) {
75 auto cs = bmp.info().colorSpace()->makeLinearGamma();
76 // Promote to F16 color type to preserve precision.
77 auto ii = SkImageInfo::Make(srcW, srcH, kRGBA_F16_SkColorType, bmp.info().alphaType(),
79 auto linearSurf = SkSurface::MakeRaster(ii);
81 callback(context, nullptr);
84 linearSurf->getCanvas()->drawImage(bmp.asImage().get(), -srcX, -srcY, sampling, &paint);
85 tempSurf = std::move(linearSurf);
86 srcImage = tempSurf->makeImageSnapshot();
89 constraint = SkCanvas::kFast_SrcRectConstraint;
91 // MakeFromBitmap would trigger a copy if bmp is mutable.
92 srcImage = SkImage::MakeFromRaster(bmp.pixmap(), nullptr, nullptr);
94 while (stepsX || stepsY) {
95 int nextW = resultInfo.width();
96 int nextH = resultInfo.height();
98 nextW = resultInfo.width() << (-stepsX - 1);
100 } else if (stepsX != 0) {
107 nextH = resultInfo.height() << (-stepsY - 1);
109 } else if (stepsY != 0) {
115 auto ii = srcImage->imageInfo().makeWH(nextW, nextH);
116 if (!stepsX && !stepsY) {
117 // Might as well fold conversion to final info in the last step.
120 auto next = SkSurface::MakeRaster(ii);
122 callback(context, nullptr);
125 next->getCanvas()->drawImageRect(
126 srcImage.get(), SkRect::Make(SkIRect::MakeXYWH(srcX, srcY, srcW, srcH)),
127 SkRect::MakeIWH(nextW, nextH), sampling, &paint, constraint);
128 tempSurf = std::move(next);
129 srcImage = tempSurf->makeImageSnapshot();
133 constraint = SkCanvas::kFast_SrcRectConstraint;
136 size_t rowBytes = resultInfo.minRowBytes();
137 std::unique_ptr<char[]> data(new char[resultInfo.height() * rowBytes]);
138 SkPixmap pm(resultInfo, data.get(), rowBytes);
139 if (srcImage->readPixels(nullptr, pm, srcX, srcY)) {
140 class Result : public SkImage::AsyncReadResult {
142 Result(std::unique_ptr<const char[]> data, size_t rowBytes)
143 : fData(std::move(data)), fRowBytes(rowBytes) {}
144 int count() const override { return 1; }
145 const void* data(int i) const override { return fData.get(); }
146 size_t rowBytes(int i) const override { return fRowBytes; }
149 std::unique_ptr<const char[]> fData;
152 callback(context, std::make_unique<Result>(std::move(data), rowBytes));
154 callback(context, nullptr);