1 /*
2  * Copyright 2015 Google Inc.
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/SkMatrix.h"
10 #include "include/private/SkTemplates.h"
11 #include "src/core/SkArenaAlloc.h"
12 #include "src/core/SkBitmapCache.h"
13 #include "src/core/SkBitmapController.h"
14 #include "src/core/SkMatrixPriv.h"
15 #include "src/core/SkMipMap.h"
16 #include "src/image/SkImage_Base.h"
17 
18 ///////////////////////////////////////////////////////////////////////////////////////////////////
19 
RequestBitmap(const SkImage_Base * image,const SkMatrix & inv,SkFilterQuality quality,SkArenaAlloc * alloc)20 SkBitmapController::State* SkBitmapController::RequestBitmap(const SkImage_Base* image,
21                                                              const SkMatrix& inv,
22                                                              SkFilterQuality quality,
23                                                              SkArenaAlloc* alloc) {
24     auto* state = alloc->make<SkBitmapController::State>(image, inv, quality);
25 
26     return state->pixmap().addr() ? state : nullptr;
27 }
28 
processHighRequest(const SkImage_Base * image)29 bool SkBitmapController::State::processHighRequest(const SkImage_Base* image) {
30     if (fQuality != kHigh_SkFilterQuality) {
31         return false;
32     }
33 
34     if (SkMatrixPriv::AdjustHighQualityFilterLevel(fInvMatrix, true) != kHigh_SkFilterQuality) {
35         fQuality = kMedium_SkFilterQuality;
36         return false;
37     }
38 
39     (void)image->getROPixels(&fResultBitmap);
40     return true;
41 }
42 
43 /*
44  *  Modulo internal errors, this should always succeed *if* the matrix is downscaling
45  *  (in this case, we have the inverse, so it succeeds if fInvMatrix is upscaling)
46  */
processMediumRequest(const SkImage_Base * image)47 bool SkBitmapController::State::processMediumRequest(const SkImage_Base* image) {
48     SkASSERT(fQuality <= kMedium_SkFilterQuality);
49     if (fQuality != kMedium_SkFilterQuality) {
50         return false;
51     }
52 
53     // Our default return state is to downgrade the request to Low, w/ or w/o setting fBitmap
54     // to a valid bitmap.
55     fQuality = kLow_SkFilterQuality;
56 
57     SkSize invScaleSize;
58     if (!fInvMatrix.decomposeScale(&invScaleSize, nullptr)) {
59         return false;
60     }
61 
62     if (invScaleSize.width() > SK_Scalar1 || invScaleSize.height() > SK_Scalar1) {
63         fCurrMip.reset(SkMipMapCache::FindAndRef(SkBitmapCacheDesc::Make(image)));
64         if (nullptr == fCurrMip.get()) {
65             fCurrMip.reset(SkMipMapCache::AddAndRef(image));
66             if (nullptr == fCurrMip.get()) {
67                 return false;
68             }
69         }
70         // diagnostic for a crasher...
71         SkASSERT_RELEASE(fCurrMip->data());
72 
73         const SkSize scale = SkSize::Make(SkScalarInvert(invScaleSize.width()),
74                                           SkScalarInvert(invScaleSize.height()));
75         SkMipMap::Level level;
76         if (fCurrMip->extractLevel(scale, &level)) {
77             const SkSize& invScaleFixup = level.fScale;
78             fInvMatrix.postScale(invScaleFixup.width(), invScaleFixup.height());
79 
80             // todo: if we could wrap the fCurrMip in a pixelref, then we could just install
81             //       that here, and not need to explicitly track it ourselves.
82             return fResultBitmap.installPixels(level.fPixmap);
83         } else {
84             // failed to extract, so release the mipmap
85             fCurrMip.reset(nullptr);
86         }
87     }
88     return false;
89 }
90 
State(const SkImage_Base * image,const SkMatrix & inv,SkFilterQuality qual)91 SkBitmapController::State::State(const SkImage_Base* image,
92                                  const SkMatrix& inv,
93                                  SkFilterQuality qual) {
94     fInvMatrix = inv;
95     fQuality = qual;
96 
97     if (this->processHighRequest(image) || this->processMediumRequest(image)) {
98         SkASSERT(fResultBitmap.getPixels());
99     } else {
100         (void)image->getROPixels(&fResultBitmap);
101     }
102 
103     // fResultBitmap.getPixels() may be null, but our caller knows to check fPixmap.addr()
104     // and will destroy us if it is nullptr.
105     fPixmap.reset(fResultBitmap.info(), fResultBitmap.getPixels(), fResultBitmap.rowBytes());
106 }
107