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 inputOffset, int inputDimension, | |
21 int imageOriginalDimension, int* imageSubsetOffset, int* outOffset, | |
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, inputOffset); | |
26 | |
27 // If inputOffset is less than zero, we decode to an offset location in the
output bitmap. | |
28 *outOffset = *imageSubsetOffset - inputOffset; | |
29 | |
30 // Use imageSusetOffset to make sure we don't decode pixels past the edge of
the image. | |
31 // Use outOffset to make sure we don't decode pixels past the edge of the re
gion. | |
32 *imageSubsetDimension = SkTMin(imageOriginalDimension - *imageSubsetOffset, | |
33 inputDimension - *outOffset); | |
34 } | |
35 | |
36 /* | |
37 * Returns a scaled dimension based on the original dimension and the sample siz
e. | |
38 * TODO: Share this implementation with SkScaledCodec. | |
39 */ | |
40 static int get_scaled_dimension(int srcDimension, int sampleSize) { | |
41 if (sampleSize > srcDimension) { | |
42 return 1; | |
43 } | |
44 return srcDimension / sampleSize; | |
45 } | |
46 | |
47 /* | |
48 * Three differences from the Android version: | |
49 * Returns a Skia bitmap instead of an Android bitmap. | |
50 * Android version attempts to reuse a recycled bitmap. | |
51 * Removed the options object and used parameters for color type and | |
52 * sample size. | |
53 */ | |
54 SkBitmap* SkBitmapRegionCanvas::decodeRegion(int inputX, int inputY, | |
55 int inputWidth, int inputHeight, | |
56 int sampleSize, | |
57 SkColorType dstColorType) { | |
58 // Reject color types not supported by this method | |
59 if (kIndex_8_SkColorType == dstColorType || kGray_8_SkColorType == dstColorT
ype) { | |
60 SkDebugf("Error: Color type not supported.\n"); | |
61 return nullptr; | |
62 } | |
63 | |
64 // The client may not necessarily request a region that is fully within | |
65 // the image. We may need to do some calculation to determine what part | |
66 // of the image to decode. | |
67 | |
68 // The left offset of the portion of the image we want, where zero | |
69 // indicates the left edge of the image. | |
70 int imageSubsetX; | |
71 | |
72 // The size of the output bitmap is determined by the size of the | |
73 // requested region, not by the size of the intersection of the region | |
74 // and the image dimensions. If inputX is negative, we will need to | |
75 // place decoded pixels into the output bitmap starting at a left offset. | |
76 // If this is non-zero, imageSubsetX must be zero. | |
77 int outX; | |
78 | |
79 // The width of the portion of the image that we will write to the output | |
80 // bitmap. If the region is not fully contained within the image, this | |
81 // will not be the same as inputWidth. | |
82 int imageSubsetWidth; | |
83 set_subset_region(inputX, inputWidth, this->width(), &imageSubsetX, &outX, &
imageSubsetWidth); | |
84 | |
85 // The top offset of the portion of the image we want, where zero | |
86 // indicates the top edge of the image. | |
87 int imageSubsetY; | |
88 | |
89 // The size of the output bitmap is determined by the size of the | |
90 // requested region, not by the size of the intersection of the region | |
91 // and the image dimensions. If inputY is negative, we will need to | |
92 // place decoded pixels into the output bitmap starting at a top offset. | |
93 // If this is non-zero, imageSubsetY must be zero. | |
94 int outY; | |
95 | |
96 // The height of the portion of the image that we will write to the output | |
97 // bitmap. If the region is not fully contained within the image, this | |
98 // will not be the same as inputHeight. | |
99 int imageSubsetHeight; | |
100 set_subset_region(inputY, inputHeight, this->height(), &imageSubsetY, &outY, | |
101 &imageSubsetHeight); | |
102 | |
103 if (imageSubsetWidth <= 0 || imageSubsetHeight <= 0) { | |
104 SkDebugf("Error: Region must intersect part of the image.\n"); | |
105 return nullptr; | |
106 } | |
107 | |
108 // Create the image info for the decode | |
109 SkAlphaType dstAlphaType = fDecoder->getInfo().alphaType(); | |
110 if (kUnpremul_SkAlphaType == dstAlphaType) { | |
111 dstAlphaType = kPremul_SkAlphaType; | |
112 } | |
113 SkImageInfo decodeInfo = SkImageInfo::Make(this->width(), this->height(), | |
114 dstColorType, dstAlphaType); | |
115 | |
116 // Start the scanline decoder | |
117 SkCodec::Result r = fDecoder->start(decodeInfo); | |
118 if (SkCodec::kSuccess != r) { | |
119 SkDebugf("Error: Could not start scanline decoder.\n"); | |
120 return nullptr; | |
121 } | |
122 | |
123 // Allocate a bitmap for the unscaled decode | |
124 SkBitmap tmp; | |
125 SkImageInfo tmpInfo = decodeInfo.makeWH(this->width(), imageSubsetHeight); | |
126 if (!tmp.tryAllocPixels(tmpInfo)) { | |
127 SkDebugf("Error: Could not allocate pixels.\n"); | |
128 return nullptr; | |
129 } | |
130 | |
131 // Skip the unneeded rows | |
132 if (SkCodec::kSuccess != fDecoder->skipScanlines(imageSubsetY)) { | |
133 SkDebugf("Error: Failed to skip scanlines.\n"); | |
134 return nullptr; | |
135 } | |
136 | |
137 // Decode the necessary rows | |
138 SkCodec::Result result = fDecoder->getScanlines(tmp.getAddr(0, 0), imageSubs
etHeight, | |
139 tmp.rowBytes()); | |
140 switch (result) { | |
141 case SkCodec::kSuccess: | |
142 case SkCodec::kIncompleteInput: | |
143 break; | |
144 default: | |
145 SkDebugf("Error: Failed to get scanlines.\n"); | |
146 return nullptr; | |
147 } | |
148 | |
149 // Calculate the size of the output | |
150 const int outWidth = get_scaled_dimension(inputWidth, sampleSize); | |
151 const int outHeight = get_scaled_dimension(inputHeight, sampleSize); | |
152 | |
153 // Initialize the destination bitmap | |
154 SkAutoTDelete<SkBitmap> bitmap(new SkBitmap()); | |
155 SkImageInfo dstInfo = decodeInfo.makeWH(outWidth, outHeight); | |
156 if (!bitmap->tryAllocPixels(dstInfo)) { | |
157 SkDebugf("Error: Could not allocate pixels.\n"); | |
158 return nullptr; | |
159 } | |
160 | |
161 // Zero the bitmap if the region is not completely within the image. | |
162 // TODO (msarett): Can we make this faster by implementing it to only | |
163 // zero parts of the image that we won't overwrite with | |
164 // pixels? | |
165 // TODO (msarett): This could be skipped if memory is zero initialized. | |
166 // This would matter if this code is moved to Android and | |
167 // uses Android bitmaps. | |
168 if (0 != outX || 0 != outY || | |
169 inputX + inputWidth > this->width() || | |
170 inputY + inputHeight > this->height()) { | |
171 bitmap->eraseColor(0); | |
172 } | |
173 | |
174 // Use a canvas to crop and scale to the destination bitmap | |
175 SkCanvas canvas(*bitmap); | |
176 // TODO (msarett): Maybe we can take advantage of the fact that SkRect uses
floats? | |
177 SkRect src = SkRect::MakeXYWH((SkScalar) imageSubsetX, (SkScalar) 0, | |
178 (SkScalar) imageSubsetWidth, (SkScalar) imageSubsetHeight); | |
179 SkRect dst = SkRect::MakeXYWH((SkScalar) (outX / sampleSize), (SkScalar) (ou
tY / sampleSize), | |
180 (SkScalar) get_scaled_dimension(imageSubsetWidth, sampleSize), | |
181 (SkScalar) get_scaled_dimension(imageSubsetHeight, sampleSize)); | |
182 SkPaint paint; | |
183 // Overwrite the dst with the src pixels | |
184 paint.setXfermodeMode(SkXfermode::kSrc_Mode); | |
185 // TODO (msarett): Test multiple filter qualities. kNone is the default. | |
186 canvas.drawBitmapRect(tmp, src, dst, &paint); | |
187 | |
188 return bitmap.detach(); | |
189 } | |
OLD | NEW |