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