| 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 "SkCodec.h" | |
| 9 #include "SkCodecPriv.h" | |
| 10 #include "SkSampledCodec.h" | |
| 11 | |
| 12 // FIXME: Rename this file to SkSampledCodec.cpp | |
| 13 | |
| 14 SkSampledCodec::SkSampledCodec(SkCodec* codec) | |
| 15 : INHERITED(codec->getInfo()) | |
| 16 , fCodec(codec) | |
| 17 {} | |
| 18 | |
| 19 SkISize SkSampledCodec::onGetSampledDimensions(int sampleSize) const { | |
| 20 // Fast path for when we are not scaling. | |
| 21 if (1 == sampleSize) { | |
| 22 return fCodec->getInfo().dimensions(); | |
| 23 } | |
| 24 | |
| 25 const int width = fCodec->getInfo().width(); | |
| 26 const int height = fCodec->getInfo().height(); | |
| 27 | |
| 28 // Check if the codec can provide the scaling natively. | |
| 29 float scale = get_scale_from_sample_size(sampleSize); | |
| 30 SkSize idealSize = SkSize::Make(scale * (float) width, scale * (float) heigh
t); | |
| 31 SkISize nativeSize = fCodec->getScaledDimensions(scale); | |
| 32 float widthDiff = SkTAbs(((float) nativeSize.width()) - idealSize.width()); | |
| 33 float heightDiff = SkTAbs(((float) nativeSize.height()) - idealSize.height()
); | |
| 34 // Native scaling is preferred to sampling. If we can scale natively to | |
| 35 // within one of the ideal value, we should choose to scale natively. | |
| 36 if (widthDiff < 1.0f && heightDiff < 1.0f) { | |
| 37 return nativeSize; | |
| 38 } | |
| 39 | |
| 40 // Provide the scaling by sampling. | |
| 41 return SkISize::Make(get_scaled_dimension(width, sampleSize), | |
| 42 get_scaled_dimension(height, sampleSize)); | |
| 43 } | |
| 44 | |
| 45 SkCodec::Result SkSampledCodec::onGetAndroidPixels(const SkImageInfo& info, void
* pixels, | |
| 46 size_t rowBytes, AndroidOptions& options) { | |
| 47 // Create an Options struct for the codec. | |
| 48 SkCodec::Options codecOptions; | |
| 49 codecOptions.fZeroInitialized = options.fZeroInitialized; | |
| 50 | |
| 51 SkIRect* subset = options.fSubset; | |
| 52 if (!subset || subset->size() == fCodec->getInfo().dimensions()) { | |
| 53 if (fCodec->dimensionsSupported(info.dimensions())) { | |
| 54 return fCodec->getPixels(info, pixels, rowBytes, &codecOptions, opti
ons.fColorPtr, | |
| 55 options.fColorCount); | |
| 56 } | |
| 57 | |
| 58 // If the native codec does not support the requested scale, scale by sa
mpling. | |
| 59 return this->sampledDecode(info, pixels, rowBytes, options); | |
| 60 } | |
| 61 | |
| 62 // We are performing a subset decode. | |
| 63 int sampleSize = options.fSampleSize; | |
| 64 SkISize scaledSize = this->onGetSampledDimensions(sampleSize); | |
| 65 if (!fCodec->dimensionsSupported(scaledSize)) { | |
| 66 // If the native codec does not support the requested scale, scale by sa
mpling. | |
| 67 return this->sampledDecode(info, pixels, rowBytes, options); | |
| 68 } | |
| 69 | |
| 70 // Calculate the scaled subset bounds. | |
| 71 int scaledSubsetX = subset->x() / sampleSize; | |
| 72 int scaledSubsetY = subset->y() / sampleSize; | |
| 73 int scaledSubsetWidth = info.width(); | |
| 74 int scaledSubsetHeight = info.height(); | |
| 75 | |
| 76 // Start the scanline decode. | |
| 77 SkIRect scanlineSubset = SkIRect::MakeXYWH(scaledSubsetX, 0, scaledSubsetWid
th, | |
| 78 scaledSize.height()); | |
| 79 codecOptions.fSubset = &scanlineSubset; | |
| 80 SkCodec::Result result = fCodec->startScanlineDecode(info.makeWH(scaledSize.
width(), | |
| 81 scaledSize.height()), &codecOptions, options.fColorPtr, options.fCol
orCount); | |
| 82 if (SkCodec::kSuccess != result) { | |
| 83 return result; | |
| 84 } | |
| 85 | |
| 86 // At this point, we are only concerned with subsetting. Either no scale wa
s | |
| 87 // requested, or the fCodec is handling the scale. | |
| 88 switch (fCodec->getScanlineOrder()) { | |
| 89 case SkCodec::kTopDown_SkScanlineOrder: | |
| 90 case SkCodec::kNone_SkScanlineOrder: { | |
| 91 if (!fCodec->skipScanlines(scaledSubsetY)) { | |
| 92 fCodec->fillIncompleteImage(info, pixels, rowBytes, options.fZer
oInitialized, | |
| 93 scaledSubsetHeight, 0); | |
| 94 return SkCodec::kIncompleteInput; | |
| 95 } | |
| 96 | |
| 97 int decodedLines = fCodec->getScanlines(pixels, scaledSubsetHeight,
rowBytes); | |
| 98 if (decodedLines != scaledSubsetHeight) { | |
| 99 return SkCodec::kIncompleteInput; | |
| 100 } | |
| 101 return SkCodec::kSuccess; | |
| 102 } | |
| 103 default: | |
| 104 SkASSERT(false); | |
| 105 return SkCodec::kUnimplemented; | |
| 106 } | |
| 107 } | |
| 108 | |
| 109 | |
| 110 SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pix
els, | |
| 111 size_t rowBytes, AndroidOptions& options) { | |
| 112 // Create options struct for the codec. | |
| 113 SkCodec::Options sampledOptions; | |
| 114 sampledOptions.fZeroInitialized = options.fZeroInitialized; | |
| 115 | |
| 116 // Check if there is a subset. | |
| 117 SkIRect subset; | |
| 118 int subsetY = 0; | |
| 119 int subsetWidth = fCodec->getInfo().width(); | |
| 120 int subsetHeight = fCodec->getInfo().height(); | |
| 121 if (options.fSubset) { | |
| 122 // We will need to know about subsetting in the y-dimension in order to
use the | |
| 123 // scanline decoder. | |
| 124 SkIRect* subsetPtr = options.fSubset; | |
| 125 subsetY = subsetPtr->y(); | |
| 126 subsetWidth = subsetPtr->width(); | |
| 127 subsetHeight = subsetPtr->height(); | |
| 128 | |
| 129 // The scanline decoder only needs to be aware of subsetting in the x-di
mension. | |
| 130 subset.setXYWH(subsetPtr->x(), 0, subsetWidth, fCodec->getInfo().height(
)); | |
| 131 sampledOptions.fSubset = ⊂ | |
| 132 } | |
| 133 | |
| 134 // Start the scanline decode. | |
| 135 SkCodec::Result result = fCodec->startScanlineDecode( | |
| 136 info.makeWH(fCodec->getInfo().width(), fCodec->getInfo().height()),
&sampledOptions, | |
| 137 options.fColorPtr, options.fColorCount); | |
| 138 if (SkCodec::kSuccess != result) { | |
| 139 return result; | |
| 140 } | |
| 141 | |
| 142 SkSampler* sampler = fCodec->getSampler(true); | |
| 143 if (!sampler) { | |
| 144 return SkCodec::kUnimplemented; | |
| 145 } | |
| 146 | |
| 147 // Since we guarantee that output dimensions are always at least one (even i
f the sampleSize | |
| 148 // is greater than a given dimension), the input sampleSize is not always th
e sampleSize that | |
| 149 // we use in practice. | |
| 150 const int sampleX = subsetWidth / info.width(); | |
| 151 const int sampleY = subsetHeight / info.height(); | |
| 152 if (sampler->setSampleX(sampleX) != info.width()) { | |
| 153 return SkCodec::kInvalidScale; | |
| 154 } | |
| 155 if (get_scaled_dimension(subsetHeight, sampleY) != info.height()) { | |
| 156 return SkCodec::kInvalidScale; | |
| 157 } | |
| 158 | |
| 159 const int samplingOffsetY = get_start_coord(sampleY); | |
| 160 const int startY = samplingOffsetY + subsetY; | |
| 161 int dstHeight = info.height(); | |
| 162 switch(fCodec->getScanlineOrder()) { | |
| 163 case SkCodec::kTopDown_SkScanlineOrder: { | |
| 164 if (!fCodec->skipScanlines(startY)) { | |
| 165 fCodec->fillIncompleteImage(info, pixels, rowBytes, options.fZer
oInitialized, | |
| 166 dstHeight, 0); | |
| 167 return SkCodec::kIncompleteInput; | |
| 168 } | |
| 169 void* pixelPtr = pixels; | |
| 170 for (int y = 0; y < dstHeight; y++) { | |
| 171 if (1 != fCodec->getScanlines(pixelPtr, 1, rowBytes)) { | |
| 172 fCodec->fillIncompleteImage(info, pixels, rowBytes, options.
fZeroInitialized, | |
| 173 dstHeight, y + 1); | |
| 174 return SkCodec::kIncompleteInput; | |
| 175 } | |
| 176 int linesToSkip = SkTMin(sampleY - 1, dstHeight - y - 1); | |
| 177 if (!fCodec->skipScanlines(linesToSkip)) { | |
| 178 fCodec->fillIncompleteImage(info, pixels, rowBytes, options.
fZeroInitialized, | |
| 179 dstHeight, y + 1); | |
| 180 return SkCodec::kIncompleteInput; | |
| 181 } | |
| 182 pixelPtr = SkTAddOffset<void>(pixelPtr, rowBytes); | |
| 183 } | |
| 184 return SkCodec::kSuccess; | |
| 185 } | |
| 186 case SkCodec::kNone_SkScanlineOrder: { | |
| 187 const int linesNeeded = subsetHeight - samplingOffsetY; | |
| 188 SkAutoMalloc storage(linesNeeded * rowBytes); | |
| 189 uint8_t* storagePtr = static_cast<uint8_t*>(storage.get()); | |
| 190 | |
| 191 if (!fCodec->skipScanlines(startY)) { | |
| 192 fCodec->fillIncompleteImage(info, pixels, rowBytes, options.fZer
oInitialized, | |
| 193 dstHeight, 0); | |
| 194 return SkCodec::kIncompleteInput; | |
| 195 } | |
| 196 int scanlines = fCodec->getScanlines(storagePtr, linesNeeded, rowByt
es); | |
| 197 | |
| 198 for (int y = 0; y < dstHeight; y++) { | |
| 199 memcpy(pixels, storagePtr, info.minRowBytes()); | |
| 200 storagePtr += sampleY * rowBytes; | |
| 201 pixels = SkTAddOffset<void>(pixels, rowBytes); | |
| 202 } | |
| 203 | |
| 204 if (scanlines < dstHeight) { | |
| 205 // fCodec has already handled filling uninitialized memory. | |
| 206 return SkCodec::kIncompleteInput; | |
| 207 } | |
| 208 return SkCodec::kSuccess; | |
| 209 } | |
| 210 default: | |
| 211 SkASSERT(false); | |
| 212 return SkCodec::kUnimplemented; | |
| 213 } | |
| 214 } | |
| OLD | NEW |