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
20SkBitmapController::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
29bool 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 */
47bool 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
91SkBitmapController::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