1 /*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "ImageDecoder.h"
18
19 #include <hwui/Bitmap.h>
20
21 #include <SkAndroidCodec.h>
22 #include <SkCanvas.h>
23 #include <SkPaint.h>
24
25 using namespace android;
26
getDefaultColorSpace() const27 sk_sp<SkColorSpace> ImageDecoder::getDefaultColorSpace() const {
28 const skcms_ICCProfile* encodedProfile = mCodec->getICCProfile();
29 if (encodedProfile) {
30 // If the profile maps directly to an SkColorSpace, that SkColorSpace
31 // will be returned. Otherwise, nullptr will be returned. In either
32 // case, using this SkColorSpace results in doing no color correction.
33 return SkColorSpace::Make(*encodedProfile);
34 }
35
36 // The image has no embedded color profile, and should be treated as SRGB.
37 return SkColorSpace::MakeSRGB();
38 }
39
ImageDecoder(std::unique_ptr<SkAndroidCodec> codec,sk_sp<SkPngChunkReader> peeker)40 ImageDecoder::ImageDecoder(std::unique_ptr<SkAndroidCodec> codec, sk_sp<SkPngChunkReader> peeker)
41 : mCodec(std::move(codec))
42 , mPeeker(std::move(peeker))
43 , mTargetSize(mCodec->getInfo().dimensions())
44 , mDecodeSize(mTargetSize)
45 , mOutColorType(mCodec->computeOutputColorType(kN32_SkColorType))
46 , mUnpremultipliedRequired(false)
47 , mOutColorSpace(getDefaultColorSpace())
48 , mSampleSize(1)
49 {
50 }
51
getOutAlphaType() const52 SkAlphaType ImageDecoder::getOutAlphaType() const {
53 return opaque() ? kOpaque_SkAlphaType
54 : mUnpremultipliedRequired ? kUnpremul_SkAlphaType : kPremul_SkAlphaType;
55 }
56
setTargetSize(int width,int height)57 bool ImageDecoder::setTargetSize(int width, int height) {
58 if (width <= 0 || height <= 0) {
59 return false;
60 }
61
62 auto info = SkImageInfo::Make(width, height, mOutColorType, getOutAlphaType());
63 size_t rowBytes = info.minRowBytes();
64 if (rowBytes == 0) {
65 // This would have overflowed.
66 return false;
67 }
68
69 size_t pixelMemorySize;
70 if (!Bitmap::computeAllocationSize(rowBytes, height, &pixelMemorySize)) {
71 return false;
72 }
73
74 if (mCropRect) {
75 if (mCropRect->right() > width || mCropRect->bottom() > height) {
76 return false;
77 }
78 }
79
80 SkISize targetSize = { width, height }, decodeSize = targetSize;
81 int sampleSize = mCodec->computeSampleSize(&decodeSize);
82
83 if (decodeSize != targetSize && mUnpremultipliedRequired && !opaque()) {
84 return false;
85 }
86
87 mTargetSize = targetSize;
88 mDecodeSize = decodeSize;
89 mSampleSize = sampleSize;
90 return true;
91 }
92
setCropRect(const SkIRect * crop)93 bool ImageDecoder::setCropRect(const SkIRect* crop) {
94 if (!crop) {
95 mCropRect.reset();
96 return true;
97 }
98
99 if (crop->left() >= crop->right() || crop->top() >= crop->bottom()) {
100 return false;
101 }
102
103 const auto& size = mTargetSize;
104 if (crop->left() < 0 || crop->top() < 0
105 || crop->right() > size.width() || crop->bottom() > size.height()) {
106 return false;
107 }
108
109 mCropRect.emplace(*crop);
110 return true;
111 }
112
setOutColorType(SkColorType colorType)113 bool ImageDecoder::setOutColorType(SkColorType colorType) {
114 switch (colorType) {
115 case kRGB_565_SkColorType:
116 if (!opaque()) {
117 return false;
118 }
119 break;
120 case kGray_8_SkColorType:
121 if (!gray()) {
122 return false;
123 }
124 break;
125 case kN32_SkColorType:
126 break;
127 case kRGBA_F16_SkColorType:
128 break;
129 default:
130 return false;
131 }
132
133 mOutColorType = colorType;
134 return true;
135 }
136
setUnpremultipliedRequired(bool required)137 bool ImageDecoder::setUnpremultipliedRequired(bool required) {
138 if (required && !opaque() && mDecodeSize != mTargetSize) {
139 return false;
140 }
141 mUnpremultipliedRequired = required;
142 return true;
143 }
144
setOutColorSpace(sk_sp<SkColorSpace> colorSpace)145 void ImageDecoder::setOutColorSpace(sk_sp<SkColorSpace> colorSpace) {
146 mOutColorSpace = std::move(colorSpace);
147 }
148
getOutputColorSpace() const149 sk_sp<SkColorSpace> ImageDecoder::getOutputColorSpace() const {
150 // kGray_8 is used for ALPHA_8, which ignores the color space.
151 return mOutColorType == kGray_8_SkColorType ? nullptr : mOutColorSpace;
152 }
153
154
getOutputInfo() const155 SkImageInfo ImageDecoder::getOutputInfo() const {
156 SkISize size = mCropRect ? mCropRect->size() : mTargetSize;
157 return SkImageInfo::Make(size, mOutColorType, getOutAlphaType(), getOutputColorSpace());
158 }
159
opaque() const160 bool ImageDecoder::opaque() const {
161 return mCodec->getInfo().alphaType() == kOpaque_SkAlphaType;
162 }
163
gray() const164 bool ImageDecoder::gray() const {
165 return mCodec->getInfo().colorType() == kGray_8_SkColorType;
166 }
167
decode(void * pixels,size_t rowBytes)168 SkCodec::Result ImageDecoder::decode(void* pixels, size_t rowBytes) {
169 void* decodePixels = pixels;
170 size_t decodeRowBytes = rowBytes;
171 auto decodeInfo = SkImageInfo::Make(mDecodeSize, mOutColorType, getOutAlphaType(),
172 getOutputColorSpace());
173 // Used if we need a temporary before scaling or subsetting.
174 // FIXME: Use scanline decoding on only a couple lines to save memory. b/70709380.
175 SkBitmap tmp;
176 const bool scale = mDecodeSize != mTargetSize;
177 if (scale || mCropRect) {
178 if (!tmp.setInfo(decodeInfo)) {
179 return SkCodec::kInternalError;
180 }
181 if (!Bitmap::allocateHeapBitmap(&tmp)) {
182 return SkCodec::kInternalError;
183 }
184 decodePixels = tmp.getPixels();
185 decodeRowBytes = tmp.rowBytes();
186 }
187
188 SkAndroidCodec::AndroidOptions options;
189 options.fSampleSize = mSampleSize;
190 auto result = mCodec->getAndroidPixels(decodeInfo, decodePixels, decodeRowBytes, &options);
191
192 if (scale || mCropRect) {
193 SkBitmap scaledBm;
194 if (!scaledBm.installPixels(getOutputInfo(), pixels, rowBytes)) {
195 return SkCodec::kInternalError;
196 }
197
198 SkPaint paint;
199 paint.setBlendMode(SkBlendMode::kSrc);
200 paint.setFilterQuality(kLow_SkFilterQuality); // bilinear filtering
201
202 SkCanvas canvas(scaledBm, SkCanvas::ColorBehavior::kLegacy);
203 if (mCropRect) {
204 canvas.translate(-mCropRect->fLeft, -mCropRect->fTop);
205 }
206 if (scale) {
207 float scaleX = (float) mTargetSize.width() / mDecodeSize.width();
208 float scaleY = (float) mTargetSize.height() / mDecodeSize.height();
209 canvas.scale(scaleX, scaleY);
210 }
211
212 canvas.drawBitmap(tmp, 0.0f, 0.0f, &paint);
213 }
214
215 return result;
216 }
217
218