aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/android/SkBitmapRegionCanvas.cpp
blob: c7c42bd9a25c0bfa759178e9c3b04e6dad0bfff8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/*
 * Copyright 2015 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkBitmapRegionCanvas.h"
#include "SkBitmapRegionDecoderPriv.h"
#include "SkCanvas.h"
#include "SkCodecPriv.h"

SkBitmapRegionCanvas::SkBitmapRegionCanvas(SkCodec* decoder)
    : INHERITED(decoder->getInfo().width(), decoder->getInfo().height())
    , fDecoder(decoder)
{}

bool SkBitmapRegionCanvas::decodeRegion(SkBitmap* bitmap, SkBRDAllocator* allocator,
        const SkIRect& desiredSubset, int sampleSize, SkColorType dstColorType,
        bool requireUnpremul) {

    // Reject color types not supported by this method
    if (kIndex_8_SkColorType == dstColorType || kGray_8_SkColorType == dstColorType) {
        SkCodecPrintf("Error: Color type not supported.\n");
        return false;
    }

    // Reject requests for unpremultiplied alpha
    if (requireUnpremul) {
        SkCodecPrintf("Error: Alpha type not supported.\n");
        return false;
    }
    SkAlphaType dstAlphaType = fDecoder->getInfo().alphaType();
    if (kUnpremul_SkAlphaType == dstAlphaType) {
        dstAlphaType = kPremul_SkAlphaType;
    }

    // Fix the input sampleSize if necessary.
    if (sampleSize < 1) {
        sampleSize = 1;
    }

    // The size of the output bitmap is determined by the size of the
    // requested subset, not by the size of the intersection of the subset
    // and the image dimensions.
    // If inputX is negative, we will need to place decoded pixels into the
    // output bitmap starting at a left offset.  Call this outX.
    // If outX is non-zero, subsetX must be zero.
    // If inputY is negative, we will need to place decoded pixels into the
    // output bitmap starting at a top offset.  Call this outY.
    // If outY is non-zero, subsetY must be zero.
    int outX;
    int outY;
    SkIRect subset = desiredSubset;
    SubsetType type = adjust_subset_rect(fDecoder->getInfo().dimensions(), &subset, &outX, &outY);
    if (SubsetType::kOutside_SubsetType == type) {
        return false;
    }

    // Create the image info for the decode
    SkImageInfo decodeInfo = SkImageInfo::Make(this->width(), this->height(),
            dstColorType, dstAlphaType);

    // Start the scanline decoder
    SkCodec::Result r = fDecoder->startScanlineDecode(decodeInfo);
    if (SkCodec::kSuccess != r) {
        SkCodecPrintf("Error: Could not start scanline decoder.\n");
        return false;
    }

    // Allocate a bitmap for the unscaled decode
    SkBitmap tmp;
    SkImageInfo tmpInfo = decodeInfo.makeWH(this->width(), subset.height());
    if (!tmp.tryAllocPixels(tmpInfo)) {
        SkCodecPrintf("Error: Could not allocate pixels.\n");
        return false;
    }

    // Skip the unneeded rows
    if (!fDecoder->skipScanlines(subset.y())) {
        SkCodecPrintf("Error: Failed to skip scanlines.\n");
        return false;
    }

    // Decode the necessary rows
    fDecoder->getScanlines(tmp.getAddr(0, 0), subset.height(), tmp.rowBytes());

    // Calculate the size of the output
    const int outWidth = get_scaled_dimension(desiredSubset.width(), sampleSize);
    const int outHeight = get_scaled_dimension(desiredSubset.height(), sampleSize);

    // Initialize the destination bitmap
    SkImageInfo dstInfo = decodeInfo.makeWH(outWidth, outHeight);
    bitmap->setInfo(dstInfo, dstInfo.minRowBytes());
    if (!bitmap->tryAllocPixels(allocator, nullptr)) {
        SkCodecPrintf("Error: Could not allocate pixels.\n");
        return false;
    }

    // Zero the bitmap if the region is not completely within the image.
    // TODO (msarett): Can we make this faster by implementing it to only
    //                 zero parts of the image that we won't overwrite with
    //                 pixels?
    if (SubsetType::kPartiallyInside_SubsetType == type) {
        SkCodec::ZeroInitialized zeroInit = allocator ? allocator->zeroInit() :
                    SkCodec::kNo_ZeroInitialized;
        if (SkCodec::kNo_ZeroInitialized == zeroInit) {
            bitmap->eraseColor(0);
        }
    }

    // Use a canvas to crop and scale to the destination bitmap
    SkCanvas canvas(*bitmap);
    // TODO (msarett): Maybe we can take advantage of the fact that SkRect uses floats?
    SkRect src = SkRect::MakeXYWH((SkScalar) subset.x(), (SkScalar) 0,
            (SkScalar) subset.width(), (SkScalar) subset.height());
    SkRect dst = SkRect::MakeXYWH((SkScalar) (outX / sampleSize), (SkScalar) (outY / sampleSize),
            (SkScalar) get_scaled_dimension(subset.width(), sampleSize),
            (SkScalar) get_scaled_dimension(subset.height(), sampleSize));
    SkPaint paint;
    // Overwrite the dst with the src pixels
    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
    // TODO (msarett): Test multiple filter qualities.  kNone is the default.
    canvas.drawBitmapRect(tmp, src, dst, &paint);

    return true;
}

bool SkBitmapRegionCanvas::conversionSupported(SkColorType colorType) {
    // SkCanvas does not draw to these color types.
    if (kIndex_8_SkColorType == colorType || kGray_8_SkColorType == colorType) {
        return false;
    }

    // FIXME: Call virtual function when it lands.
    SkImageInfo info = SkImageInfo::Make(0, 0, colorType, fDecoder->getInfo().alphaType(),
            fDecoder->getInfo().profileType());
    return conversion_possible(info, fDecoder->getInfo());
}