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