Chromium Code Reviews| 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 |