OLD | NEW |
---|---|
(Empty) | |
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 "SkCanvas.h" | |
10 #include "SkScanlineDecoder.h" | |
11 | |
12 SkBitmapRegionCanvas::SkBitmapRegionCanvas(SkScanlineDecoder* decoder) | |
13 : INHERITED(decoder->getInfo().width(), decoder->getInfo().height()) | |
14 , fDecoder(decoder) | |
15 {} | |
16 | |
17 /* | |
18 * Chooses the correct image subset offsets and dimensions for the partial decod e. | |
19 */ | |
20 static inline void set_subset_region(int regionOffset, int regionDimension, | |
21 int imageOriginalDimension, int* imageSubsetOffset, int* bitmapOffset, | |
22 int* imageSubsetDimension) { | |
23 | |
24 // This must be at least zero, we can't start decoding the image at a negati ve coordinate. | |
25 *imageSubsetOffset = SkTMax(0, regionOffset); | |
26 | |
27 // If regionOffset is less than zero, we decode to an offset location in the output bitmap. | |
28 *bitmapOffset = *imageSubsetOffset - regionOffset; | |
29 | |
30 // Use imageSusetOffset to make sure we don't decode pixels past the edge of the image. | |
31 // Use bitmapOffset to make sure we don't decode pixels past the edge of the region. | |
32 *imageSubsetDimension = SkTMin(imageOriginalDimension - *imageSubsetOffset, | |
33 regionDimension - *bitmapOffset); | |
34 } | |
35 | |
36 static int get_scaled_dimension(int dimension, int sampleSize) { | |
37 return SkTMax(1, dimension / sampleSize); | |
38 } | |
39 | |
40 /* | |
41 * Three differences from the Android version: | |
42 * Returns a Skia bitmap instead of an Android bitmap. | |
43 * Android version attempts to reuse a recycled bitmap. | |
44 * Removed the options object and used parameters for color type and | |
45 * sample size. | |
46 */ | |
47 SkBitmap* SkBitmapRegionCanvas::decodeRegion(int regionLeft, int regionTop, | |
scroggo
2015/09/04 17:55:06
I know Android started using this term "region", b
msarett
2015/09/04 18:54:10
Will do. And I'm replacing Left and Top in all of
| |
48 int regionWidth, int regionHeight, | |
49 int sampleSize, | |
50 SkColorType dstColorType) { | |
51 // Reject color types not supported by this method | |
52 if (kIndex_8_SkColorType == dstColorType || kGray_8_SkColorType == dstColorT ype) { | |
53 SkDebugf("Error: Color type not supported.\n"); | |
54 return nullptr; | |
55 } | |
56 | |
57 // The client may not necessarily request a region that is fully within | |
scroggo
2015/09/04 17:55:07
I find these comments very helpful :)
msarett
2015/09/04 18:54:10
Great! Third try is the charm haha.
| |
58 // the image. We may need to do some calculation to determine what part | |
59 // of the image to decode. | |
60 | |
61 // The left offset of the portion of the image we want, where zero | |
62 // indicates the left edge of the image. | |
63 int imageSubsetLeft; | |
64 // The size of the output bitmap is determined by the size of the | |
scroggo
2015/09/04 17:55:07
nit: blank line between code and large comment blo
msarett
2015/09/04 18:54:10
Done.
| |
65 // requested region, not by the size of the intersection of the region | |
66 // and the image dimensions. If regionLeft is negative, we will need to | |
67 // place decoded pixels into the output bitmap starting at a left offset. | |
68 // If this is non-zero, imageSubsetLeft must be zero. | |
69 int bitmapLeft; | |
scroggo
2015/09/04 17:55:06
Maybe this would be better named outLeft? Somethin
msarett
2015/09/04 18:54:10
Yes agreed.
| |
70 // The width of the portion of the image that we will write to the output | |
71 // bitmap. If the region is not fully contained within the image, this | |
72 // will not be the same as regionWidth. | |
73 int imageSubsetWidth; | |
74 set_subset_region(regionLeft, regionWidth, this->width(), &imageSubsetLeft, &bitmapLeft, | |
75 &imageSubsetWidth); | |
76 | |
77 // The top offset of the portion of the image we want, where zero | |
78 // indicates the top edge of the image. | |
79 int imageSubsetTop; | |
80 // The size of the output bitmap is determined by the size of the | |
81 // requested region, not by the size of the intersection of the region | |
82 // and the image dimensions. If regionTop is negative, we will need to | |
83 // place decoded pixels into the output bitmap starting at a top offset. | |
84 // If this is non-zero, imageSubsetTop must be zero. | |
85 int bitmapTop; | |
86 // The height of the portion of the image that we will write to the output | |
87 // bitmap. If the region is not fully contained within the image, this | |
88 // will not be the same as regionHeight. | |
89 int imageSubsetHeight; | |
90 set_subset_region(regionTop, regionHeight, this->height(), &imageSubsetTop, &bitmapTop, | |
91 &imageSubsetHeight); | |
92 | |
93 if (imageSubsetWidth <= 0 || imageSubsetHeight <= 0) { | |
94 SkDebugf("Error: Region must intersect part of the image.\n"); | |
95 return nullptr; | |
96 } | |
97 | |
98 // Create the image info for the decode | |
99 SkAlphaType dstAlphaType = fDecoder->getInfo().alphaType(); | |
100 if (kUnpremul_SkAlphaType == dstAlphaType) { | |
101 dstAlphaType = kPremul_SkAlphaType; | |
102 } | |
103 SkImageInfo decodeInfo = SkImageInfo::Make(this->width(), this->height(), | |
104 dstColorType, dstAlphaType); | |
105 | |
106 // Start the scanline decoder | |
107 SkCodec::Result r = fDecoder->start(decodeInfo); | |
108 if (SkCodec::kSuccess != r) { | |
109 SkDebugf("Error: Could not start scanline decoder.\n"); | |
110 return nullptr; | |
111 } | |
112 | |
113 // Allocate a bitmap for the unscaled decode | |
114 SkBitmap tmp; | |
115 SkImageInfo tmpInfo = decodeInfo.makeWH(this->width(), imageSubsetHeight); | |
116 if (!tmp.tryAllocPixels(tmpInfo)) { | |
117 SkDebugf("Error: Could not allocate pixels.\n"); | |
118 return nullptr; | |
119 } | |
120 | |
121 // Skip the unneeded rows | |
122 if (SkCodec::kSuccess != fDecoder->skipScanlines(imageSubsetTop)) { | |
123 SkDebugf("Error: Failed to skip scanlines.\n"); | |
124 return nullptr; | |
125 } | |
126 | |
127 // Decode the necessary rows | |
128 SkCodec::Result result = fDecoder->getScanlines(tmp.getAddr(0, 0), imageSubs etHeight, | |
129 tmp.rowBytes()); | |
130 switch (result) { | |
131 case SkCodec::kSuccess: | |
132 case SkCodec::kIncompleteInput: | |
133 break; | |
134 default: | |
135 SkDebugf("Error: Failed to get scanlines.\n"); | |
136 return nullptr; | |
137 } | |
138 | |
139 // Calculate the size of the output | |
140 const int outWidth = get_scaled_dimension(regionWidth, sampleSize); | |
141 const int outHeight = get_scaled_dimension(regionHeight, sampleSize); | |
142 | |
143 // Initialize the destination bitmap | |
144 SkAutoTDelete<SkBitmap> bitmap(new SkBitmap()); | |
145 SkImageInfo dstInfo = decodeInfo.makeWH(outWidth, outHeight); | |
146 if (!bitmap->tryAllocPixels(dstInfo)) { | |
147 SkDebugf("Error: Could not allocate pixels.\n"); | |
148 return nullptr; | |
149 } | |
150 | |
151 // Zero the bitmap if the region is not completely within the image. | |
152 // TODO (msarett): Can we make this faster by implementing it to only | |
153 // zero parts of the image that we won't overwrite with | |
154 // pixels? | |
155 // TODO (msarett): This could be skipped if memory is zero initialized. | |
156 // This would matter if this code is moved to Android and | |
157 // uses Android bitmaps. | |
158 if (0 != bitmapLeft || 0 != bitmapTop || | |
159 regionLeft + regionWidth > this->width() || | |
160 regionTop + regionHeight > this->height()) { | |
161 bitmap->eraseColor(0); | |
162 } | |
163 | |
164 // Use a canvas to crop and scale to the destination bitmap | |
165 SkCanvas canvas(*bitmap); | |
166 SkRect src = SkRect::MakeXYWH(imageSubsetLeft, 0, imageSubsetWidth, imageSub setHeight); | |
167 SkRect dst = SkRect::MakeXYWH(bitmapLeft / sampleSize, bitmapTop / sampleSiz e, | |
168 get_scaled_dimension(imageSubsetWidth, sampleSize), | |
169 get_scaled_dimension(imageSubsetHeight, sampleSize)); | |
170 SkPaint paint; | |
171 // Overwrite the dst with the src pixels | |
172 paint.setXfermodeMode(SkXfermode::kSrc_Mode); | |
173 // TODO (msarett): Test multiple filter qualities. kNone is the default. | |
174 canvas.drawBitmapRect(tmp, src, dst, &paint); | |
175 | |
176 return bitmap.detach(); | |
177 } | |
OLD | NEW |