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