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 "SkCodec.h" 9 #include "SkCodecPriv.h" 10 #include "SkMath.h" 11 #include "SkSampledCodec.h" 12 #include "SkSampler.h" 13 #include "SkTemplates.h" 14 15 SkSampledCodec::SkSampledCodec(SkCodec* codec) 16 : INHERITED(codec) 17 {} 18 19 SkISize SkSampledCodec::accountForNativeScaling(int* sampleSizePtr, int* nativeSampleSize) const { 20 SkISize preSampledSize = this->codec()->getInfo().dimensions(); 21 int sampleSize = *sampleSizePtr; 22 SkASSERT(sampleSize > 1); 23 24 if (nativeSampleSize) { 25 *nativeSampleSize = 1; 26 } 27 28 // Only JPEG supports native downsampling. 29 if (this->codec()->getEncodedFormat() == kJPEG_SkEncodedFormat) { 30 // See if libjpeg supports this scale directly 31 switch (sampleSize) { 32 case 2: 33 case 4: 34 case 8: 35 // This class does not need to do any sampling. 36 *sampleSizePtr = 1; 37 return this->codec()->getScaledDimensions(get_scale_from_sample_size(sampleSize)); 38 default: 39 break; 40 } 41 42 // Check if sampleSize is a multiple of something libjpeg can support. 43 int remainder; 44 const int sampleSizes[] = { 8, 4, 2 }; 45 for (int supportedSampleSize : sampleSizes) { 46 int actualSampleSize; 47 SkTDivMod(sampleSize, supportedSampleSize, &actualSampleSize, &remainder); 48 if (0 == remainder) { 49 float scale = get_scale_from_sample_size(supportedSampleSize); 50 51 // this->codec() will scale to this size. 52 preSampledSize = this->codec()->getScaledDimensions(scale); 53 54 // And then this class will sample it. 55 *sampleSizePtr = actualSampleSize; 56 if (nativeSampleSize) { 57 *nativeSampleSize = supportedSampleSize; 58 } 59 break; 60 } 61 } 62 } 63 64 return preSampledSize; 65 } 66 67 SkISize SkSampledCodec::onGetSampledDimensions(int sampleSize) const { 68 const SkISize size = this->accountForNativeScaling(&sampleSize); 69 return SkISize::Make(get_scaled_dimension(size.width(), sampleSize), 70 get_scaled_dimension(size.height(), sampleSize)); 71 } 72 73 SkCodec::Result SkSampledCodec::onGetAndroidPixels(const SkImageInfo& info, void* pixels, 74 size_t rowBytes, const AndroidOptions& options) { 75 // Create an Options struct for the codec. 76 SkCodec::Options codecOptions; 77 codecOptions.fZeroInitialized = options.fZeroInitialized; 78 79 SkIRect* subset = options.fSubset; 80 if (!subset || subset->size() == this->codec()->getInfo().dimensions()) { 81 if (this->codec()->dimensionsSupported(info.dimensions())) { 82 return this->codec()->getPixels(info, pixels, rowBytes, &codecOptions, 83 options.fColorPtr, options.fColorCount); 84 } 85 86 // If the native codec does not support the requested scale, scale by sampling. 87 return this->sampledDecode(info, pixels, rowBytes, options); 88 } 89 90 // We are performing a subset decode. 91 int sampleSize = options.fSampleSize; 92 SkISize scaledSize = this->getSampledDimensions(sampleSize); 93 if (!this->codec()->dimensionsSupported(scaledSize)) { 94 // If the native codec does not support the requested scale, scale by sampling. 95 return this->sampledDecode(info, pixels, rowBytes, options); 96 } 97 98 // Calculate the scaled subset bounds. 99 int scaledSubsetX = subset->x() / sampleSize; 100 int scaledSubsetY = subset->y() / sampleSize; 101 int scaledSubsetWidth = info.width(); 102 int scaledSubsetHeight = info.height(); 103 104 // Start the scanline decode. 105 SkIRect scanlineSubset = SkIRect::MakeXYWH(scaledSubsetX, 0, scaledSubsetWidth, 106 scaledSize.height()); 107 codecOptions.fSubset = &scanlineSubset; 108 SkCodec::Result result = this->codec()->startScanlineDecode(info.makeWH(scaledSize.width(), 109 scaledSize.height()), &codecOptions, options.fColorPtr, options.fColorCount); 110 if (SkCodec::kSuccess != result) { 111 return result; 112 } 113 114 // At this point, we are only concerned with subsetting. Either no scale was 115 // requested, or the this->codec() is handling the scale. 116 switch (this->codec()->getScanlineOrder()) { 117 case SkCodec::kTopDown_SkScanlineOrder: 118 case SkCodec::kNone_SkScanlineOrder: { 119 if (!this->codec()->skipScanlines(scaledSubsetY)) { 120 this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized, 121 scaledSubsetHeight, 0); 122 return SkCodec::kIncompleteInput; 123 } 124 125 int decodedLines = this->codec()->getScanlines(pixels, scaledSubsetHeight, rowBytes); 126 if (decodedLines != scaledSubsetHeight) { 127 return SkCodec::kIncompleteInput; 128 } 129 return SkCodec::kSuccess; 130 } 131 default: 132 SkASSERT(false); 133 return SkCodec::kUnimplemented; 134 } 135 } 136 137 138 SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pixels, 139 size_t rowBytes, const AndroidOptions& options) { 140 // We should only call this function when sampling. 141 SkASSERT(options.fSampleSize > 1); 142 143 // Create options struct for the codec. 144 SkCodec::Options sampledOptions; 145 sampledOptions.fZeroInitialized = options.fZeroInitialized; 146 147 // FIXME: This was already called by onGetAndroidPixels. Can we reduce that? 148 int sampleSize = options.fSampleSize; 149 int nativeSampleSize; 150 SkISize nativeSize = this->accountForNativeScaling(&sampleSize, &nativeSampleSize); 151 152 // Check if there is a subset. 153 SkIRect subset; 154 int subsetY = 0; 155 int subsetWidth = nativeSize.width(); 156 int subsetHeight = nativeSize.height(); 157 if (options.fSubset) { 158 // We will need to know about subsetting in the y-dimension in order to use the 159 // scanline decoder. 160 // Update the subset to account for scaling done by this->codec(). 161 SkIRect* subsetPtr = options.fSubset; 162 163 // Do the divide ourselves, instead of calling get_scaled_dimension. If 164 // X and Y are 0, they should remain 0, rather than being upgraded to 1 165 // due to being smaller than the sampleSize. 166 const int subsetX = subsetPtr->x() / nativeSampleSize; 167 subsetY = subsetPtr->y() / nativeSampleSize; 168 169 subsetWidth = get_scaled_dimension(subsetPtr->width(), nativeSampleSize); 170 subsetHeight = get_scaled_dimension(subsetPtr->height(), nativeSampleSize); 171 172 // The scanline decoder only needs to be aware of subsetting in the x-dimension. 173 subset.setXYWH(subsetX, 0, subsetWidth, nativeSize.height()); 174 sampledOptions.fSubset = ⊂ 175 } 176 177 // Start the scanline decode. 178 SkCodec::Result result = this->codec()->startScanlineDecode( 179 info.makeWH(nativeSize.width(), nativeSize.height()), &sampledOptions, 180 options.fColorPtr, options.fColorCount); 181 if (SkCodec::kSuccess != result) { 182 return result; 183 } 184 185 SkSampler* sampler = this->codec()->getSampler(true); 186 if (!sampler) { 187 return SkCodec::kUnimplemented; 188 } 189 190 // Since we guarantee that output dimensions are always at least one (even if the sampleSize 191 // is greater than a given dimension), the input sampleSize is not always the sampleSize that 192 // we use in practice. 193 const int sampleX = subsetWidth / info.width(); 194 const int sampleY = subsetHeight / info.height(); 195 if (sampler->setSampleX(sampleX) != info.width()) { 196 return SkCodec::kInvalidScale; 197 } 198 if (get_scaled_dimension(subsetHeight, sampleY) != info.height()) { 199 return SkCodec::kInvalidScale; 200 } 201 202 const int samplingOffsetY = get_start_coord(sampleY); 203 const int startY = samplingOffsetY + subsetY; 204 int dstHeight = info.height(); 205 switch(this->codec()->getScanlineOrder()) { 206 case SkCodec::kTopDown_SkScanlineOrder: { 207 if (!this->codec()->skipScanlines(startY)) { 208 this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized, 209 dstHeight, 0); 210 return SkCodec::kIncompleteInput; 211 } 212 void* pixelPtr = pixels; 213 for (int y = 0; y < dstHeight; y++) { 214 if (1 != this->codec()->getScanlines(pixelPtr, 1, rowBytes)) { 215 this->codec()->fillIncompleteImage(info, pixels, rowBytes, 216 options.fZeroInitialized, dstHeight, y + 1); 217 return SkCodec::kIncompleteInput; 218 } 219 if (y < dstHeight - 1) { 220 if (!this->codec()->skipScanlines(sampleY - 1)) { 221 this->codec()->fillIncompleteImage(info, pixels, rowBytes, 222 options.fZeroInitialized, dstHeight, y + 1); 223 return SkCodec::kIncompleteInput; 224 } 225 } 226 pixelPtr = SkTAddOffset<void>(pixelPtr, rowBytes); 227 } 228 return SkCodec::kSuccess; 229 } 230 case SkCodec::kOutOfOrder_SkScanlineOrder: 231 case SkCodec::kBottomUp_SkScanlineOrder: { 232 // Note that these modes do not support subsetting. 233 SkASSERT(0 == subsetY && nativeSize.height() == subsetHeight); 234 int y; 235 for (y = 0; y < nativeSize.height(); y++) { 236 int srcY = this->codec()->nextScanline(); 237 if (is_coord_necessary(srcY, sampleY, dstHeight)) { 238 void* pixelPtr = SkTAddOffset<void>(pixels, 239 rowBytes * get_dst_coord(srcY, sampleY)); 240 if (1 != this->codec()->getScanlines(pixelPtr, 1, rowBytes)) { 241 break; 242 } 243 } else { 244 if (!this->codec()->skipScanlines(1)) { 245 break; 246 } 247 } 248 } 249 250 if (nativeSize.height() == y) { 251 return SkCodec::kSuccess; 252 } 253 254 // We handle filling uninitialized memory here instead of using this->codec(). 255 // this->codec() does not know that we are sampling. 256 const uint32_t fillValue = this->codec()->getFillValue(info.colorType()); 257 const SkImageInfo fillInfo = info.makeWH(info.width(), 1); 258 for (; y < nativeSize.height(); y++) { 259 int srcY = this->codec()->outputScanline(y); 260 if (!is_coord_necessary(srcY, sampleY, dstHeight)) { 261 continue; 262 } 263 264 void* rowPtr = SkTAddOffset<void>(pixels, rowBytes * get_dst_coord(srcY, sampleY)); 265 SkSampler::Fill(fillInfo, rowPtr, rowBytes, fillValue, options.fZeroInitialized); 266 } 267 return SkCodec::kIncompleteInput; 268 } 269 case SkCodec::kNone_SkScanlineOrder: { 270 const int linesNeeded = subsetHeight - samplingOffsetY; 271 SkAutoTMalloc<uint8_t> storage(linesNeeded * rowBytes); 272 uint8_t* storagePtr = storage.get(); 273 274 if (!this->codec()->skipScanlines(startY)) { 275 this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized, 276 dstHeight, 0); 277 return SkCodec::kIncompleteInput; 278 } 279 int scanlines = this->codec()->getScanlines(storagePtr, linesNeeded, rowBytes); 280 281 for (int y = 0; y < dstHeight; y++) { 282 memcpy(pixels, storagePtr, info.minRowBytes()); 283 storagePtr += sampleY * rowBytes; 284 pixels = SkTAddOffset<void>(pixels, rowBytes); 285 } 286 287 if (scanlines < dstHeight) { 288 // this->codec() has already handled filling uninitialized memory. 289 return SkCodec::kIncompleteInput; 290 } 291 return SkCodec::kSuccess; 292 } 293 default: 294 SkASSERT(false); 295 return SkCodec::kUnimplemented; 296 } 297 } 298