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() == SkEncodedImageFormat::kJPEG) { 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 codecOptions.fPremulBehavior = SkTransferFunctionBehavior::kIgnore; 79 80 SkIRect* subset = options.fSubset; 81 if (!subset || subset->size() == this->codec()->getInfo().dimensions()) { 82 if (this->codec()->dimensionsSupported(info.dimensions())) { 83 return this->codec()->getPixels(info, pixels, rowBytes, &codecOptions, 84 options.fColorPtr, options.fColorCount); 85 } 86 87 // If the native codec does not support the requested scale, scale by sampling. 88 return this->sampledDecode(info, pixels, rowBytes, options); 89 } 90 91 // We are performing a subset decode. 92 int sampleSize = options.fSampleSize; 93 SkISize scaledSize = this->getSampledDimensions(sampleSize); 94 if (!this->codec()->dimensionsSupported(scaledSize)) { 95 // If the native codec does not support the requested scale, scale by sampling. 96 return this->sampledDecode(info, pixels, rowBytes, options); 97 } 98 99 // Calculate the scaled subset bounds. 100 int scaledSubsetX = subset->x() / sampleSize; 101 int scaledSubsetY = subset->y() / sampleSize; 102 int scaledSubsetWidth = info.width(); 103 int scaledSubsetHeight = info.height(); 104 105 const SkImageInfo scaledInfo = info.makeWH(scaledSize.width(), scaledSize.height()); 106 107 { 108 // Although startScanlineDecode expects the bottom and top to match the 109 // SkImageInfo, startIncrementalDecode uses them to determine which rows to 110 // decode. 111 SkIRect incrementalSubset = SkIRect::MakeXYWH(scaledSubsetX, scaledSubsetY, 112 scaledSubsetWidth, scaledSubsetHeight); 113 codecOptions.fSubset = &incrementalSubset; 114 const SkCodec::Result startResult = this->codec()->startIncrementalDecode( 115 scaledInfo, pixels, rowBytes, &codecOptions, 116 options.fColorPtr, options.fColorCount); 117 if (SkCodec::kSuccess == startResult) { 118 int rowsDecoded; 119 const SkCodec::Result incResult = this->codec()->incrementalDecode(&rowsDecoded); 120 if (incResult == SkCodec::kSuccess) { 121 return SkCodec::kSuccess; 122 } 123 SkASSERT(SkCodec::kIncompleteInput == incResult); 124 125 // FIXME: Can zero initialized be read from SkCodec::fOptions? 126 this->codec()->fillIncompleteImage(scaledInfo, pixels, rowBytes, 127 options.fZeroInitialized, scaledSubsetHeight, rowsDecoded); 128 return SkCodec::kIncompleteInput; 129 } else if (startResult != SkCodec::kUnimplemented) { 130 return startResult; 131 } 132 // Otherwise fall down to use the old scanline decoder. 133 // codecOptions.fSubset will be reset below, so it will not continue to 134 // point to the object that is no longer on the stack. 135 } 136 137 // Start the scanline decode. 138 SkIRect scanlineSubset = SkIRect::MakeXYWH(scaledSubsetX, 0, scaledSubsetWidth, 139 scaledSize.height()); 140 codecOptions.fSubset = &scanlineSubset; 141 142 SkCodec::Result result = this->codec()->startScanlineDecode(scaledInfo, 143 &codecOptions, options.fColorPtr, options.fColorCount); 144 if (SkCodec::kSuccess != result) { 145 return result; 146 } 147 148 // At this point, we are only concerned with subsetting. Either no scale was 149 // requested, or the this->codec() is handling the scale. 150 // Note that subsetting is only supported for kTopDown, so this code will not be 151 // reached for other orders. 152 SkASSERT(this->codec()->getScanlineOrder() == SkCodec::kTopDown_SkScanlineOrder); 153 if (!this->codec()->skipScanlines(scaledSubsetY)) { 154 this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized, 155 scaledSubsetHeight, 0); 156 return SkCodec::kIncompleteInput; 157 } 158 159 int decodedLines = this->codec()->getScanlines(pixels, scaledSubsetHeight, rowBytes); 160 if (decodedLines != scaledSubsetHeight) { 161 return SkCodec::kIncompleteInput; 162 } 163 return SkCodec::kSuccess; 164 } 165 166 167 SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pixels, 168 size_t rowBytes, const AndroidOptions& options) { 169 // We should only call this function when sampling. 170 SkASSERT(options.fSampleSize > 1); 171 172 // Create options struct for the codec. 173 SkCodec::Options sampledOptions; 174 sampledOptions.fZeroInitialized = options.fZeroInitialized; 175 sampledOptions.fPremulBehavior = SkTransferFunctionBehavior::kIgnore; 176 177 // FIXME: This was already called by onGetAndroidPixels. Can we reduce that? 178 int sampleSize = options.fSampleSize; 179 int nativeSampleSize; 180 SkISize nativeSize = this->accountForNativeScaling(&sampleSize, &nativeSampleSize); 181 182 // Check if there is a subset. 183 SkIRect subset; 184 int subsetY = 0; 185 int subsetWidth = nativeSize.width(); 186 int subsetHeight = nativeSize.height(); 187 if (options.fSubset) { 188 // We will need to know about subsetting in the y-dimension in order to use the 189 // scanline decoder. 190 // Update the subset to account for scaling done by this->codec(). 191 const SkIRect* subsetPtr = options.fSubset; 192 193 // Do the divide ourselves, instead of calling get_scaled_dimension. If 194 // X and Y are 0, they should remain 0, rather than being upgraded to 1 195 // due to being smaller than the sampleSize. 196 const int subsetX = subsetPtr->x() / nativeSampleSize; 197 subsetY = subsetPtr->y() / nativeSampleSize; 198 199 subsetWidth = get_scaled_dimension(subsetPtr->width(), nativeSampleSize); 200 subsetHeight = get_scaled_dimension(subsetPtr->height(), nativeSampleSize); 201 202 // The scanline decoder only needs to be aware of subsetting in the x-dimension. 203 subset.setXYWH(subsetX, 0, subsetWidth, nativeSize.height()); 204 sampledOptions.fSubset = ⊂ 205 } 206 207 // Since we guarantee that output dimensions are always at least one (even if the sampleSize 208 // is greater than a given dimension), the input sampleSize is not always the sampleSize that 209 // we use in practice. 210 const int sampleX = subsetWidth / info.width(); 211 const int sampleY = subsetHeight / info.height(); 212 213 const int samplingOffsetY = get_start_coord(sampleY); 214 const int startY = samplingOffsetY + subsetY; 215 const int dstHeight = info.height(); 216 217 const SkImageInfo nativeInfo = info.makeWH(nativeSize.width(), nativeSize.height()); 218 219 { 220 // Although startScanlineDecode expects the bottom and top to match the 221 // SkImageInfo, startIncrementalDecode uses them to determine which rows to 222 // decode. 223 SkCodec::Options incrementalOptions = sampledOptions; 224 SkIRect incrementalSubset; 225 if (sampledOptions.fSubset) { 226 incrementalSubset.fTop = subsetY; 227 incrementalSubset.fBottom = subsetY + subsetHeight; 228 incrementalSubset.fLeft = sampledOptions.fSubset->fLeft; 229 incrementalSubset.fRight = sampledOptions.fSubset->fRight; 230 incrementalOptions.fSubset = &incrementalSubset; 231 } 232 const SkCodec::Result startResult = this->codec()->startIncrementalDecode(nativeInfo, 233 pixels, rowBytes, &incrementalOptions, options.fColorPtr, options.fColorCount); 234 if (SkCodec::kSuccess == startResult) { 235 SkSampler* sampler = this->codec()->getSampler(true); 236 if (!sampler) { 237 return SkCodec::kUnimplemented; 238 } 239 240 if (sampler->setSampleX(sampleX) != info.width()) { 241 return SkCodec::kInvalidScale; 242 } 243 if (get_scaled_dimension(subsetHeight, sampleY) != info.height()) { 244 return SkCodec::kInvalidScale; 245 } 246 247 sampler->setSampleY(sampleY); 248 249 int rowsDecoded; 250 const SkCodec::Result incResult = this->codec()->incrementalDecode(&rowsDecoded); 251 if (incResult == SkCodec::kSuccess) { 252 return SkCodec::kSuccess; 253 } 254 SkASSERT(incResult == SkCodec::kIncompleteInput); 255 256 SkASSERT(rowsDecoded <= info.height()); 257 this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized, 258 info.height(), rowsDecoded); 259 return SkCodec::kIncompleteInput; 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, options.fColorPtr, options.fColorCount); 268 if (SkCodec::kSuccess != result) { 269 return result; 270 } 271 272 SkSampler* sampler = this->codec()->getSampler(true); 273 if (!sampler) { 274 return SkCodec::kUnimplemented; 275 } 276 277 if (sampler->setSampleX(sampleX) != info.width()) { 278 return SkCodec::kInvalidScale; 279 } 280 if (get_scaled_dimension(subsetHeight, sampleY) != info.height()) { 281 return SkCodec::kInvalidScale; 282 } 283 284 switch(this->codec()->getScanlineOrder()) { 285 case SkCodec::kTopDown_SkScanlineOrder: { 286 if (!this->codec()->skipScanlines(startY)) { 287 this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized, 288 dstHeight, 0); 289 return SkCodec::kIncompleteInput; 290 } 291 void* pixelPtr = pixels; 292 for (int y = 0; y < dstHeight; y++) { 293 if (1 != this->codec()->getScanlines(pixelPtr, 1, rowBytes)) { 294 this->codec()->fillIncompleteImage(info, pixels, rowBytes, 295 options.fZeroInitialized, dstHeight, y + 1); 296 return SkCodec::kIncompleteInput; 297 } 298 if (y < dstHeight - 1) { 299 if (!this->codec()->skipScanlines(sampleY - 1)) { 300 this->codec()->fillIncompleteImage(info, pixels, rowBytes, 301 options.fZeroInitialized, dstHeight, y + 1); 302 return SkCodec::kIncompleteInput; 303 } 304 } 305 pixelPtr = SkTAddOffset<void>(pixelPtr, rowBytes); 306 } 307 return SkCodec::kSuccess; 308 } 309 case SkCodec::kBottomUp_SkScanlineOrder: { 310 // Note that these modes do not support subsetting. 311 SkASSERT(0 == subsetY && nativeSize.height() == subsetHeight); 312 int y; 313 for (y = 0; y < nativeSize.height(); y++) { 314 int srcY = this->codec()->nextScanline(); 315 if (is_coord_necessary(srcY, sampleY, dstHeight)) { 316 void* pixelPtr = SkTAddOffset<void>(pixels, 317 rowBytes * get_dst_coord(srcY, sampleY)); 318 if (1 != this->codec()->getScanlines(pixelPtr, 1, rowBytes)) { 319 break; 320 } 321 } else { 322 if (!this->codec()->skipScanlines(1)) { 323 break; 324 } 325 } 326 } 327 328 if (nativeSize.height() == y) { 329 return SkCodec::kSuccess; 330 } 331 332 // We handle filling uninitialized memory here instead of using this->codec(). 333 // this->codec() does not know that we are sampling. 334 const uint64_t fillValue = this->codec()->getFillValue(info); 335 const SkImageInfo fillInfo = info.makeWH(info.width(), 1); 336 for (; y < nativeSize.height(); y++) { 337 int srcY = this->codec()->outputScanline(y); 338 if (!is_coord_necessary(srcY, sampleY, dstHeight)) { 339 continue; 340 } 341 342 void* rowPtr = SkTAddOffset<void>(pixels, rowBytes * get_dst_coord(srcY, sampleY)); 343 SkSampler::Fill(fillInfo, rowPtr, rowBytes, fillValue, options.fZeroInitialized); 344 } 345 return SkCodec::kIncompleteInput; 346 } 347 default: 348 SkASSERT(false); 349 return SkCodec::kUnimplemented; 350 } 351 } 352