• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "SkBitmapRegionCanvas.h"
9 #include "SkBitmapRegionDecoderPriv.h"
10 #include "SkCanvas.h"
11 #include "SkCodecPriv.h"
12 
SkBitmapRegionCanvas(SkCodec * decoder)13 SkBitmapRegionCanvas::SkBitmapRegionCanvas(SkCodec* decoder)
14     : INHERITED(decoder->getInfo().width(), decoder->getInfo().height())
15     , fDecoder(decoder)
16 {}
17 
decodeRegion(SkBitmap * bitmap,SkBRDAllocator * allocator,const SkIRect & desiredSubset,int sampleSize,SkColorType dstColorType,bool requireUnpremul)18 bool SkBitmapRegionCanvas::decodeRegion(SkBitmap* bitmap, SkBRDAllocator* allocator,
19         const SkIRect& desiredSubset, int sampleSize, SkColorType dstColorType,
20         bool requireUnpremul) {
21 
22     // Reject color types not supported by this method
23     if (kIndex_8_SkColorType == dstColorType || kGray_8_SkColorType == dstColorType) {
24         SkCodecPrintf("Error: Color type not supported.\n");
25         return false;
26     }
27 
28     // Reject requests for unpremultiplied alpha
29     if (requireUnpremul) {
30         SkCodecPrintf("Error: Alpha type not supported.\n");
31         return false;
32     }
33     SkAlphaType dstAlphaType = fDecoder->getInfo().alphaType();
34     if (kUnpremul_SkAlphaType == dstAlphaType) {
35         dstAlphaType = kPremul_SkAlphaType;
36     }
37 
38     // Fix the input sampleSize if necessary.
39     if (sampleSize < 1) {
40         sampleSize = 1;
41     }
42 
43     // The size of the output bitmap is determined by the size of the
44     // requested subset, not by the size of the intersection of the subset
45     // and the image dimensions.
46     // If inputX is negative, we will need to place decoded pixels into the
47     // output bitmap starting at a left offset.  Call this outX.
48     // If outX is non-zero, subsetX must be zero.
49     // If inputY is negative, we will need to place decoded pixels into the
50     // output bitmap starting at a top offset.  Call this outY.
51     // If outY is non-zero, subsetY must be zero.
52     int outX;
53     int outY;
54     SkIRect subset = desiredSubset;
55     SubsetType type = adjust_subset_rect(fDecoder->getInfo().dimensions(), &subset, &outX, &outY);
56     if (SubsetType::kOutside_SubsetType == type) {
57         return false;
58     }
59 
60     // Create the image info for the decode
61     SkImageInfo decodeInfo = SkImageInfo::Make(this->width(), this->height(),
62             dstColorType, dstAlphaType);
63 
64     // Start the scanline decoder
65     SkCodec::Result r = fDecoder->startScanlineDecode(decodeInfo);
66     if (SkCodec::kSuccess != r) {
67         SkCodecPrintf("Error: Could not start scanline decoder.\n");
68         return false;
69     }
70 
71     // Allocate a bitmap for the unscaled decode
72     SkBitmap tmp;
73     SkImageInfo tmpInfo = decodeInfo.makeWH(this->width(), subset.height());
74     if (!tmp.tryAllocPixels(tmpInfo)) {
75         SkCodecPrintf("Error: Could not allocate pixels.\n");
76         return false;
77     }
78 
79     // Skip the unneeded rows
80     if (!fDecoder->skipScanlines(subset.y())) {
81         SkCodecPrintf("Error: Failed to skip scanlines.\n");
82         return false;
83     }
84 
85     // Decode the necessary rows
86     fDecoder->getScanlines(tmp.getAddr(0, 0), subset.height(), tmp.rowBytes());
87 
88     // Calculate the size of the output
89     const int outWidth = get_scaled_dimension(desiredSubset.width(), sampleSize);
90     const int outHeight = get_scaled_dimension(desiredSubset.height(), sampleSize);
91 
92     // Initialize the destination bitmap
93     SkImageInfo dstInfo = decodeInfo.makeWH(outWidth, outHeight);
94     bitmap->setInfo(dstInfo, dstInfo.minRowBytes());
95     if (!bitmap->tryAllocPixels(allocator, nullptr)) {
96         SkCodecPrintf("Error: Could not allocate pixels.\n");
97         return false;
98     }
99 
100     // Zero the bitmap if the region is not completely within the image.
101     // TODO (msarett): Can we make this faster by implementing it to only
102     //                 zero parts of the image that we won't overwrite with
103     //                 pixels?
104     if (SubsetType::kPartiallyInside_SubsetType == type) {
105         SkCodec::ZeroInitialized zeroInit = allocator ? allocator->zeroInit() :
106                     SkCodec::kNo_ZeroInitialized;
107         if (SkCodec::kNo_ZeroInitialized == zeroInit) {
108             bitmap->eraseColor(0);
109         }
110     }
111 
112     // Use a canvas to crop and scale to the destination bitmap
113     SkCanvas canvas(*bitmap);
114     // TODO (msarett): Maybe we can take advantage of the fact that SkRect uses floats?
115     SkRect src = SkRect::MakeXYWH((SkScalar) subset.x(), (SkScalar) 0,
116             (SkScalar) subset.width(), (SkScalar) subset.height());
117     SkRect dst = SkRect::MakeXYWH((SkScalar) (outX / sampleSize), (SkScalar) (outY / sampleSize),
118             (SkScalar) get_scaled_dimension(subset.width(), sampleSize),
119             (SkScalar) get_scaled_dimension(subset.height(), sampleSize));
120     SkPaint paint;
121     // Overwrite the dst with the src pixels
122     paint.setXfermodeMode(SkXfermode::kSrc_Mode);
123     // TODO (msarett): Test multiple filter qualities.  kNone is the default.
124     canvas.drawBitmapRect(tmp, src, dst, &paint);
125 
126     return true;
127 }
128 
conversionSupported(SkColorType colorType)129 bool SkBitmapRegionCanvas::conversionSupported(SkColorType colorType) {
130     // SkCanvas does not draw to these color types.
131     if (kIndex_8_SkColorType == colorType || kGray_8_SkColorType == colorType) {
132         return false;
133     }
134 
135     // FIXME: Call virtual function when it lands.
136     SkImageInfo info = SkImageInfo::Make(0, 0, colorType, fDecoder->getInfo().alphaType(),
137             fDecoder->getInfo().profileType());
138     return conversion_possible(info, fDecoder->getInfo());
139 }
140