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 "SkMathPriv.h" 12 #include "SkSampledCodec.h" 13 #include "SkSampler.h" 14 #include "SkTemplates.h" 15 16 SkSampledCodec::SkSampledCodec(SkCodec* codec, ExifOrientationBehavior behavior) 17 : INHERITED(codec, behavior) 18 {} 19 20 SkISize SkSampledCodec::accountForNativeScaling(int* sampleSizePtr, int* nativeSampleSize) const { 21 SkISize preSampledSize = this->codec()->dimensions(); 22 int sampleSize = *sampleSizePtr; 23 SkASSERT(sampleSize > 1); 24 25 if (nativeSampleSize) { 26 *nativeSampleSize = 1; 27 } 28 29 // Only JPEG supports native downsampling. 30 if (this->codec()->getEncodedFormat() == SkEncodedImageFormat::kJPEG) { 31 // See if libjpeg supports this scale directly 32 switch (sampleSize) { 33 case 2: 34 case 4: 35 case 8: 36 // This class does not need to do any sampling. 37 *sampleSizePtr = 1; 38 return this->codec()->getScaledDimensions(get_scale_from_sample_size(sampleSize)); 39 default: 40 break; 41 } 42 43 // Check if sampleSize is a multiple of something libjpeg can support. 44 int remainder; 45 const int sampleSizes[] = { 8, 4, 2 }; 46 for (int supportedSampleSize : sampleSizes) { 47 int actualSampleSize; 48 SkTDivMod(sampleSize, supportedSampleSize, &actualSampleSize, &remainder); 49 if (0 == remainder) { 50 float scale = get_scale_from_sample_size(supportedSampleSize); 51 52 // this->codec() will scale to this size. 53 preSampledSize = this->codec()->getScaledDimensions(scale); 54 55 // And then this class will sample it. 56 *sampleSizePtr = actualSampleSize; 57 if (nativeSampleSize) { 58 *nativeSampleSize = supportedSampleSize; 59 } 60 break; 61 } 62 } 63 } 64 65 return preSampledSize; 66 } 67 68 SkISize SkSampledCodec::onGetSampledDimensions(int sampleSize) const { 69 const SkISize size = this->accountForNativeScaling(&sampleSize); 70 return SkISize::Make(get_scaled_dimension(size.width(), sampleSize), 71 get_scaled_dimension(size.height(), sampleSize)); 72 } 73 74 SkCodec::Result SkSampledCodec::onGetAndroidPixels(const SkImageInfo& info, void* pixels, 75 size_t rowBytes, const AndroidOptions& options) { 76 // Create an Options struct for the codec. 77 SkCodec::Options codecOptions; 78 codecOptions.fZeroInitialized = options.fZeroInitialized; 79 80 SkIRect* subset = options.fSubset; 81 if (!subset || subset->size() == this->codec()->dimensions()) { 82 if (this->codec()->dimensionsSupported(info.dimensions())) { 83 return this->codec()->getPixels(info, pixels, rowBytes, &codecOptions); 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 const SkImageInfo scaledInfo = info.makeWH(scaledSize.width(), scaledSize.height()); 105 106 { 107 // Although startScanlineDecode expects the bottom and top to match the 108 // SkImageInfo, startIncrementalDecode uses them to determine which rows to 109 // decode. 110 SkIRect incrementalSubset = SkIRect::MakeXYWH(scaledSubsetX, scaledSubsetY, 111 scaledSubsetWidth, scaledSubsetHeight); 112 codecOptions.fSubset = &incrementalSubset; 113 const SkCodec::Result startResult = this->codec()->startIncrementalDecode( 114 scaledInfo, pixels, rowBytes, &codecOptions); 115 if (SkCodec::kSuccess == startResult) { 116 int rowsDecoded = 0; 117 const SkCodec::Result incResult = this->codec()->incrementalDecode(&rowsDecoded); 118 if (incResult == SkCodec::kSuccess) { 119 return SkCodec::kSuccess; 120 } 121 SkASSERT(incResult == SkCodec::kIncompleteInput || incResult == SkCodec::kErrorInInput); 122 123 // FIXME: Can zero initialized be read from SkCodec::fOptions? 124 this->codec()->fillIncompleteImage(scaledInfo, pixels, rowBytes, 125 options.fZeroInitialized, scaledSubsetHeight, rowsDecoded); 126 return incResult; 127 } else if (startResult != SkCodec::kUnimplemented) { 128 return startResult; 129 } 130 // Otherwise fall down to use the old scanline decoder. 131 // codecOptions.fSubset will be reset below, so it will not continue to 132 // point to the object that is no longer on the stack. 133 } 134 135 // Start the scanline decode. 136 SkIRect scanlineSubset = SkIRect::MakeXYWH(scaledSubsetX, 0, scaledSubsetWidth, 137 scaledSize.height()); 138 codecOptions.fSubset = &scanlineSubset; 139 140 SkCodec::Result result = this->codec()->startScanlineDecode(scaledInfo, 141 &codecOptions); 142 if (SkCodec::kSuccess != result) { 143 return result; 144 } 145 146 // At this point, we are only concerned with subsetting. Either no scale was 147 // requested, or the this->codec() is handling the scale. 148 // Note that subsetting is only supported for kTopDown, so this code will not be 149 // reached for other orders. 150 SkASSERT(this->codec()->getScanlineOrder() == SkCodec::kTopDown_SkScanlineOrder); 151 if (!this->codec()->skipScanlines(scaledSubsetY)) { 152 this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized, 153 scaledSubsetHeight, 0); 154 return SkCodec::kIncompleteInput; 155 } 156 157 int decodedLines = this->codec()->getScanlines(pixels, scaledSubsetHeight, rowBytes); 158 if (decodedLines != scaledSubsetHeight) { 159 return SkCodec::kIncompleteInput; 160 } 161 return SkCodec::kSuccess; 162 } 163 164 165 SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pixels, 166 size_t rowBytes, const AndroidOptions& options) { 167 // We should only call this function when sampling. 168 SkASSERT(options.fSampleSize > 1); 169 170 // Create options struct for the codec. 171 SkCodec::Options sampledOptions; 172 sampledOptions.fZeroInitialized = options.fZeroInitialized; 173 174 // FIXME: This was already called by onGetAndroidPixels. Can we reduce that? 175 int sampleSize = options.fSampleSize; 176 int nativeSampleSize; 177 SkISize nativeSize = this->accountForNativeScaling(&sampleSize, &nativeSampleSize); 178 179 // Check if there is a subset. 180 SkIRect subset; 181 int subsetY = 0; 182 int subsetWidth = nativeSize.width(); 183 int subsetHeight = nativeSize.height(); 184 if (options.fSubset) { 185 // We will need to know about subsetting in the y-dimension in order to use the 186 // scanline decoder. 187 // Update the subset to account for scaling done by this->codec(). 188 const SkIRect* subsetPtr = options.fSubset; 189 190 // Do the divide ourselves, instead of calling get_scaled_dimension. If 191 // X and Y are 0, they should remain 0, rather than being upgraded to 1 192 // due to being smaller than the sampleSize. 193 const int subsetX = subsetPtr->x() / nativeSampleSize; 194 subsetY = subsetPtr->y() / nativeSampleSize; 195 196 subsetWidth = get_scaled_dimension(subsetPtr->width(), nativeSampleSize); 197 subsetHeight = get_scaled_dimension(subsetPtr->height(), nativeSampleSize); 198 199 // The scanline decoder only needs to be aware of subsetting in the x-dimension. 200 subset.setXYWH(subsetX, 0, subsetWidth, nativeSize.height()); 201 sampledOptions.fSubset = ⊂ 202 } 203 204 // Since we guarantee that output dimensions are always at least one (even if the sampleSize 205 // is greater than a given dimension), the input sampleSize is not always the sampleSize that 206 // we use in practice. 207 const int sampleX = subsetWidth / info.width(); 208 const int sampleY = subsetHeight / info.height(); 209 210 const int samplingOffsetY = get_start_coord(sampleY); 211 const int startY = samplingOffsetY + subsetY; 212 const int dstHeight = info.height(); 213 214 const SkImageInfo nativeInfo = info.makeWH(nativeSize.width(), nativeSize.height()); 215 216 { 217 // Although startScanlineDecode expects the bottom and top to match the 218 // SkImageInfo, startIncrementalDecode uses them to determine which rows to 219 // decode. 220 SkCodec::Options incrementalOptions = sampledOptions; 221 SkIRect incrementalSubset; 222 if (sampledOptions.fSubset) { 223 incrementalSubset.fTop = subsetY; 224 incrementalSubset.fBottom = subsetY + subsetHeight; 225 incrementalSubset.fLeft = sampledOptions.fSubset->fLeft; 226 incrementalSubset.fRight = sampledOptions.fSubset->fRight; 227 incrementalOptions.fSubset = &incrementalSubset; 228 } 229 const SkCodec::Result startResult = this->codec()->startIncrementalDecode(nativeInfo, 230 pixels, rowBytes, &incrementalOptions); 231 if (SkCodec::kSuccess == startResult) { 232 SkSampler* sampler = this->codec()->getSampler(true); 233 if (!sampler) { 234 return SkCodec::kUnimplemented; 235 } 236 237 if (sampler->setSampleX(sampleX) != info.width()) { 238 return SkCodec::kInvalidScale; 239 } 240 if (get_scaled_dimension(subsetHeight, sampleY) != info.height()) { 241 return SkCodec::kInvalidScale; 242 } 243 244 sampler->setSampleY(sampleY); 245 246 int rowsDecoded = 0; 247 const SkCodec::Result incResult = this->codec()->incrementalDecode(&rowsDecoded); 248 if (incResult == SkCodec::kSuccess) { 249 return SkCodec::kSuccess; 250 } 251 SkASSERT(incResult == SkCodec::kIncompleteInput || incResult == SkCodec::kErrorInInput); 252 253 SkASSERT(rowsDecoded <= info.height()); 254 this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized, 255 info.height(), rowsDecoded); 256 return incResult; 257 } else if (startResult == SkCodec::kIncompleteInput 258 || startResult == SkCodec::kErrorInInput) { 259 return SkCodec::kInvalidInput; 260 } else if (startResult != SkCodec::kUnimplemented) { 261 return startResult; 262 } // kUnimplemented means use the old method. 263 } 264 265 // Start the scanline decode. 266 SkCodec::Result result = this->codec()->startScanlineDecode(nativeInfo, 267 &sampledOptions); 268 if (SkCodec::kIncompleteInput == result || SkCodec::kErrorInInput == result) { 269 return SkCodec::kInvalidInput; 270 } else if (SkCodec::kSuccess != result) { 271 return result; 272 } 273 274 SkSampler* sampler = this->codec()->getSampler(true); 275 if (!sampler) { 276 return SkCodec::kUnimplemented; 277 } 278 279 if (sampler->setSampleX(sampleX) != info.width()) { 280 return SkCodec::kInvalidScale; 281 } 282 if (get_scaled_dimension(subsetHeight, sampleY) != info.height()) { 283 return SkCodec::kInvalidScale; 284 } 285 286 switch(this->codec()->getScanlineOrder()) { 287 case SkCodec::kTopDown_SkScanlineOrder: { 288 if (!this->codec()->skipScanlines(startY)) { 289 this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized, 290 dstHeight, 0); 291 return SkCodec::kIncompleteInput; 292 } 293 void* pixelPtr = pixels; 294 for (int y = 0; y < dstHeight; y++) { 295 if (1 != this->codec()->getScanlines(pixelPtr, 1, rowBytes)) { 296 this->codec()->fillIncompleteImage(info, pixels, rowBytes, 297 options.fZeroInitialized, dstHeight, y + 1); 298 return SkCodec::kIncompleteInput; 299 } 300 if (y < dstHeight - 1) { 301 if (!this->codec()->skipScanlines(sampleY - 1)) { 302 this->codec()->fillIncompleteImage(info, pixels, rowBytes, 303 options.fZeroInitialized, dstHeight, y + 1); 304 return SkCodec::kIncompleteInput; 305 } 306 } 307 pixelPtr = SkTAddOffset<void>(pixelPtr, rowBytes); 308 } 309 return SkCodec::kSuccess; 310 } 311 case SkCodec::kBottomUp_SkScanlineOrder: { 312 // Note that these modes do not support subsetting. 313 SkASSERT(0 == subsetY && nativeSize.height() == subsetHeight); 314 int y; 315 for (y = 0; y < nativeSize.height(); y++) { 316 int srcY = this->codec()->nextScanline(); 317 if (is_coord_necessary(srcY, sampleY, dstHeight)) { 318 void* pixelPtr = SkTAddOffset<void>(pixels, 319 rowBytes * get_dst_coord(srcY, sampleY)); 320 if (1 != this->codec()->getScanlines(pixelPtr, 1, rowBytes)) { 321 break; 322 } 323 } else { 324 if (!this->codec()->skipScanlines(1)) { 325 break; 326 } 327 } 328 } 329 330 if (nativeSize.height() == y) { 331 return SkCodec::kSuccess; 332 } 333 334 // We handle filling uninitialized memory here instead of using this->codec(). 335 // this->codec() does not know that we are sampling. 336 const SkImageInfo fillInfo = info.makeWH(info.width(), 1); 337 for (; y < nativeSize.height(); y++) { 338 int srcY = this->codec()->outputScanline(y); 339 if (!is_coord_necessary(srcY, sampleY, dstHeight)) { 340 continue; 341 } 342 343 void* rowPtr = SkTAddOffset<void>(pixels, rowBytes * get_dst_coord(srcY, sampleY)); 344 SkSampler::Fill(fillInfo, rowPtr, rowBytes, options.fZeroInitialized); 345 } 346 return SkCodec::kIncompleteInput; 347 } 348 default: 349 SkASSERT(false); 350 return SkCodec::kUnimplemented; 351 } 352 } 353