SkSampledCodec.cpp 14 KB

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