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 "SkColorPriv.h" | |
11 #include "SkData.h" | |
12 #if !defined(GOOGLE3) | |
13 #include "SkJpegCodec.h" | |
14 #endif | |
15 #include "SkRawCodec.h" | |
16 #include "SkRefCnt.h" | |
17 #include "SkStream.h" | |
18 #include "SkStreamPriv.h" | |
19 #include "SkSwizzler.h" | |
20 #include "SkTemplates.h" | |
21 #include "SkTypes.h" | |
22 | |
23 #include "dng_color_space.h" | |
24 #include "dng_exceptions.h" | |
25 #include "dng_host.h" | |
26 #include "dng_info.h" | |
27 #include "dng_render.h" | |
28 #include "dng_stream.h" | |
29 | |
30 #include "src/piex.h" | |
31 | |
32 #include <cmath> // for std::round,floor,ceil | |
33 #include <limits> | |
34 | |
35 namespace { | |
36 | |
37 // T must be unsigned type. | |
38 template <class T> | |
39 bool safe_add_to_size_t(T arg1, T arg2, size_t* result) { | |
40 SkASSERT(arg1 >= 0); | |
41 SkASSERT(arg2 >= 0); | |
42 if (arg1 >= 0 && arg2 <= std::numeric_limits<T>::max() - arg1) { | |
43 T sum = arg1 + arg2; | |
44 if (sum <= std::numeric_limits<size_t>::max()) { | |
45 *result = static_cast<size_t>(sum); | |
46 return true; | |
47 } | |
48 } | |
49 return false; | |
50 } | |
51 | |
52 } // namespace | |
53 | |
54 // Note: this class could throw exception if it is used as dng_stream. | |
55 class SkRawStream : public dng_stream, public ::piex::StreamInterface { | |
56 public: | |
57 // Note that this call will take the ownership of stream. | |
58 explicit SkRawStream(SkStream* stream) | |
59 : fStream(stream), fWholeStreamRead(false) {} | |
60 | |
61 ~SkRawStream() override {} | |
62 | |
63 /* | |
64 * Creates a SkMemoryStream from the offset with size. | |
65 * Note: for performance reason, this function is destructive to the SkRawSt ream. One should | |
66 * abandon current object after the function call. | |
67 */ | |
68 SkMemoryStream* copyBuffer(size_t offset, size_t size) { | |
69 SkAutoTUnref<SkData> data(SkData::NewUninitialized(size)); | |
70 if (offset > fStreamBuffer.bytesWritten()) { | |
71 // If the offset is not buffered, read from fStream directly and ski p the buffering. | |
72 // This is destructive to currect object. | |
73 const size_t skipLength = offset - fStreamBuffer.bytesWritten(); | |
74 if (fStream->skip(skipLength) != skipLength) { | |
75 return nullptr; | |
76 } | |
77 if (fStream->read(data->writable_data(), size) != size) { | |
78 return nullptr; | |
79 } | |
80 } else { | |
81 // If the buffer is already past the offset, increase the buffer to offset + size, | |
82 // then copy the buffer to output. | |
83 size_t sum; | |
84 if (!safe_add_to_size_t(offset, size, &sum) || !this->bufferMoreData (sum)) { | |
scroggo
2016/01/13 18:24:45
Again, I don't think you need to do any buffering.
yujieqin
2016/01/14 09:33:11
Done.
| |
85 return nullptr; | |
86 } | |
87 if (!fStreamBuffer.read(data->writable_data(), offset, size)) { | |
88 return nullptr; | |
89 } | |
90 } | |
91 return new SkMemoryStream(data); | |
92 } | |
93 | |
94 // For PIEX | |
95 ::piex::Error GetData(const size_t offset, const size_t length, | |
96 uint8* data) override { | |
97 if (offset == 0 && length == 0) { | |
98 return ::piex::Error::kOk; | |
99 } | |
100 size_t sum; | |
101 if (!safe_add_to_size_t(offset, length, &sum) || !this->bufferMoreData(s um)) { | |
102 return ::piex::Error::kFail; | |
103 } | |
104 if (!fStreamBuffer.read(data, offset, length)) { | |
105 return ::piex::Error::kFail; | |
106 } | |
107 return ::piex::Error::kOk; | |
108 } | |
109 | |
110 protected: | |
111 // For dng_stream | |
112 uint64 DoGetLength() override { | |
113 if (!this->bufferMoreData(kReadToEnd)) { // read whole stream | |
114 ThrowReadFile(); | |
115 } | |
116 return fStreamBuffer.bytesWritten(); | |
117 } | |
118 | |
119 // For dng_stream | |
120 void DoRead(void* data, uint32 count, uint64 offset) override { | |
121 if (count == 0 && offset == 0) { | |
122 return; | |
123 } | |
124 size_t sum; | |
125 if (!safe_add_to_size_t(static_cast<uint64>(count), offset, &sum) || | |
126 !this->bufferMoreData(sum)) { | |
127 ThrowReadFile(); | |
128 } | |
129 | |
130 if (!fStreamBuffer.read(data, offset, count)) { | |
131 ThrowReadFile(); | |
132 } | |
133 } | |
134 | |
135 private: | |
136 // Note: if the newSize == kReadToEnd (0), this function will read to the en d of stream. | |
137 bool bufferMoreData(size_t newSize) { | |
138 if (newSize == kReadToEnd) { | |
139 if (fWholeStreamRead) { // already read-to-end. | |
140 return true; | |
141 } | |
142 | |
143 // TODO: optimize for the special case when the input is SkMemoryStr eam. | |
144 return SkStreamCopy(&fStreamBuffer, fStream.get()); | |
145 } | |
146 | |
147 if (newSize <= fStreamBuffer.bytesWritten()) { // already buffered to n ewSize | |
148 return true; | |
149 } | |
150 if (fWholeStreamRead) { // newSize is larger than the whole stream. | |
151 return false; | |
152 } | |
153 | |
154 const size_t sizeToRead = newSize - fStreamBuffer.bytesWritten(); | |
155 SkAutoTMalloc<uint8> tempBuffer(sizeToRead); | |
156 const size_t bytesRead = fStream->read(tempBuffer.get(), sizeToRead); | |
157 if (bytesRead != sizeToRead) { | |
158 return false; | |
159 } | |
160 return fStreamBuffer.write(tempBuffer.get(), bytesRead); | |
161 } | |
162 | |
163 SkAutoTDelete<SkStream> fStream; | |
164 bool fWholeStreamRead; | |
165 | |
166 SkDynamicMemoryWStream fStreamBuffer; | |
167 | |
168 const size_t kReadToEnd = 0; | |
169 }; | |
170 | |
171 class SkDngImage { | |
172 public: | |
173 static dng_negative* ReadDng(SkRawStream* stream, dng_host* host, dng_info* info) { | |
scroggo
2016/01/13 18:24:45
I think this does not need to be public?
yujieqin
2016/01/14 09:33:11
Done.
| |
174 try { | |
175 host->ValidateSizes(); | |
176 info->Parse(*host, *stream); | |
177 info->PostParse(*host); | |
178 if (!info->IsValidDNG()) { | |
179 return nullptr; | |
180 } | |
181 | |
182 SkAutoTDelete<dng_negative> negative; | |
183 negative.reset(host->Make_dng_negative()); | |
184 negative->Parse(*host, *stream, *info); | |
185 negative->PostParse(*host, *stream, *info); | |
186 negative->SynchronizeMetadata(); | |
187 return negative.release(); | |
188 } catch (...) { | |
189 return nullptr; | |
190 } | |
191 } | |
192 | |
193 static SkDngImage* NewFromStream(SkRawStream* stream) { | |
194 SkAutoTDelete<dng_host> host(new dng_host); | |
195 SkAutoTDelete<dng_info> info(new dng_info); | |
196 SkAutoTDelete<dng_negative> negative(SkDngImage::ReadDng(stream, host.ge t(), info.get())); | |
197 if (!negative) { | |
198 return nullptr; | |
199 } | |
200 return new SkDngImage(stream, host.release(), info.release(), negative.r elease()); | |
201 } | |
202 | |
203 /* | |
204 * Renders the DNG image to the size. | |
205 * The rendered image will be close to the specified size, but there is no g uarantee that any | |
206 * of the edges will match the requested size. E.g. | |
207 * 100% size: 4000 x 3000 | |
208 * requested size: 1600 x 1200 | |
209 * returned size could be: 2000 x 1500 | |
210 * | |
211 */ | |
212 SkCodec::Result render(int width, int height) { | |
scroggo
2016/01/13 18:24:45
Can this just return a boolean for success or fail
yujieqin
2016/01/14 09:33:11
Changed this method to return pointer.
| |
213 if (!fImage || | |
scroggo
2016/01/13 18:24:45
I think saving the image across calls to onGetPixe
yujieqin
2016/01/14 09:33:11
Changed the logic to return dng_image directly and
| |
214 (fImage->Size().h != width || fImage->Size().v != height)) | |
215 { | |
216 // Reset the DNG image, so that we can rerender it for different siz e. | |
217 if (fImage) { | |
218 SkCodecPrintf("Warning: For RAW images, calling render() with a size that does \ | |
219 not match the existing image size forces the decod er to render the \ | |
220 image multiple times."); | |
221 fImage.reset(); | |
222 fHost.reset(new dng_host); | |
223 fInfo.reset(new dng_info); | |
224 fNegative.reset(SkDngImage::ReadDng(fStream, fHost.get(), fInfo. get())); | |
225 if (!fNegative) { | |
226 return SkCodec::kInvalidInput; | |
227 } | |
228 } | |
229 | |
230 // DNG SDK preserves the aspect ratio, so it only needs to know the longer dimension. | |
231 const int preferredSize = SkTMax(width, height); | |
232 try { | |
233 fHost->SetPreferredSize(preferredSize); | |
234 fHost->ValidateSizes(); | |
235 | |
236 fNegative->ReadStage1Image(*fHost, *fStream, *fInfo); | |
237 | |
238 if (fInfo->fMaskIndex != -1) { | |
239 fNegative->ReadTransparencyMask(*fHost, *fStream, *fInfo); | |
240 } | |
241 | |
242 fNegative->ValidateRawImageDigest(*fHost); | |
243 if (fNegative->IsDamaged()) { | |
244 return SkCodec::kInvalidInput; | |
245 } | |
246 | |
247 const int32 kMosaicPlane = -1; | |
248 fNegative->BuildStage2Image(*fHost); | |
249 fNegative->BuildStage3Image(*fHost, kMosaicPlane); | |
250 | |
251 dng_render render(*fHost, *fNegative); | |
252 render.SetFinalSpace(dng_space_sRGB::Get()); | |
253 render.SetFinalPixelType(ttByte); | |
254 | |
255 dng_point stage3_size = fNegative->Stage3Image()->Size(); | |
256 render.SetMaximumSize(SkTMax(stage3_size.h, stage3_size.v)); | |
257 | |
258 fImage.reset(render.Render()); | |
259 | |
260 // Because the DNG SDK can not gaurantee to render to requested size, we allow a small | |
scroggo
2016/01/13 18:24:45
nit: 100 chars
yujieqin
2016/01/14 09:33:11
Done.
| |
261 // difference. Only the overlapping region will be copied in onG etPixels(). | |
262 const float maxDiffRatio = 1.02f; | |
263 const dng_point& imageSize = fImage->Size(); | |
264 if (SkTMax(width, imageSize.h) / SkTMin(width, imageSize.h) > ma xDiffRatio || | |
265 SkTMax(height, imageSize.v) / SkTMin(height, imageSize.v) > maxDiffRatio) { | |
266 return SkCodec::kInvalidScale; | |
267 } | |
268 return SkCodec::kSuccess; | |
269 } catch (...) { | |
270 fImage.reset(); | |
271 return SkCodec::kInvalidInput; | |
272 } | |
273 } | |
274 return SkCodec::kSuccess; | |
275 } | |
276 | |
277 SkCodec::Result writeToBuffer(dng_pixel_buffer* buffer) { | |
scroggo
2016/01/13 18:24:45
This only returns two values, which are essentiall
yujieqin
2016/01/14 09:33:11
Done.
| |
278 if (!fImage) { | |
279 return SkCodec::kInvalidInput; | |
280 } | |
281 | |
282 try { | |
283 fImage->Get(*buffer, dng_image::edge_zero); | |
284 return SkCodec::kSuccess; | |
285 } catch (...) { | |
286 return SkCodec::kInvalidInput; | |
287 } | |
288 } | |
289 | |
290 bool hasRenderedResult() const { | |
291 return fImage != nullptr; | |
292 } | |
293 | |
294 SkISize getRenderedSize() const { | |
295 if (!this->hasRenderedResult()) { | |
296 return SkISize::Make(0, 0); | |
297 } | |
298 SkISize dim; | |
299 dng_point imageSize = fImage->Size(); | |
300 dim.fWidth = imageSize.h; | |
301 dim.fHeight = imageSize.v; | |
302 return dim; | |
303 } | |
304 | |
305 SkImageInfo getImageInfo() const { | |
306 SkASSERT(fNegative); | |
307 return SkImageInfo::Make(fNegative->DefaultCropSizeH().As_real64(), | |
308 fNegative->DefaultCropSizeV().As_real64(), | |
309 kN32_SkColorType, kOpaque_SkAlphaType); | |
310 } | |
311 | |
312 private: | |
313 SkDngImage(SkRawStream* stream, dng_host* host, dng_info* info, dng_negative * negative) | |
314 : fStream(stream) | |
315 , fHost(host) | |
316 , fInfo(info) | |
317 , fNegative(negative) {} | |
318 | |
319 SkAutoTDelete<SkRawStream> fStream; | |
320 SkAutoTDelete<dng_host> fHost; | |
321 SkAutoTDelete<dng_info> fInfo; | |
322 SkAutoTDelete<dng_negative> fNegative; | |
323 SkAutoTDelete<dng_image> fImage; | |
324 }; | |
325 | |
326 /* | |
327 * Tries to handle the image with PIEX. If PIEX returns kOk and finds the previe w image, create a | |
328 * SkJpegCodec. If PIEX returns kFail, then the file is invalid, return nullptr. In other cases, | |
329 * fallback to create SkRawCodec for DNG images. | |
330 */ | |
331 SkCodec* SkRawCodec::NewFromStream(SkStream* stream) { | |
332 SkAutoTDelete<SkRawStream> rawStream(new SkRawStream(stream)); | |
333 ::piex::PreviewImageData imageData; | |
334 if (::piex::IsRaw(rawStream.get())) { | |
335 ::piex::Error error = ::piex::GetPreviewImageData(rawStream.get(), &imag eData); | |
336 | |
337 if (error == ::piex::Error::kOk && imageData.preview_length > 0) { | |
338 // copyBuffer() is destructive to the rawStream. Abandon the rawStre am after this | |
339 // function call. | |
340 SkMemoryStream* memoryStream = | |
scroggo
2016/01/13 18:24:45
This will leak in GOOGLE3. Instead, I think you wa
yujieqin
2016/01/14 09:33:11
Done.
| |
341 rawStream->copyBuffer(imageData.preview_offset, imageData.pr eview_length); | |
342 #if !defined(GOOGLE3) | |
343 return memoryStream ? SkJpegCodec::NewFromStream(memoryStream) : nul lptr; | |
344 #else | |
345 return nullptr; | |
346 #endif | |
347 } else if (error == ::piex::Error::kFail) { | |
348 return nullptr; | |
349 } | |
350 } | |
351 | |
352 SkAutoTDelete<SkDngImage> dngImage(SkDngImage::NewFromStream(rawStream.relea se())); | |
353 if (!dngImage) { | |
354 return nullptr; | |
355 } | |
356 | |
357 return new SkRawCodec(dngImage.release()); | |
358 } | |
359 | |
360 SkCodec::Result SkRawCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst, | |
361 size_t dstRowBytes, const Options& optio ns, | |
362 SkPMColor ctable[], int* ctableCount, | |
363 int* rowsDecoded) { | |
364 int width = requestedInfo.width(); | |
365 int height = requestedInfo.height(); | |
366 SkCodec::Result result = fDngImage->render(width, height); | |
367 if (result != kSuccess) { | |
368 return result; | |
369 } | |
370 | |
371 // Only copy the top-left of the overlapping region. | |
scroggo
2016/01/13 18:24:45
In practice, how much will be "cropped" here? If t
yujieqin
2016/01/14 09:33:11
Our experiment shows that the cropping will be les
| |
372 width = SkTMin(width, fDngImage->getRenderedSize().fWidth); | |
373 height = SkTMin(height, fDngImage->getRenderedSize().fHeight); | |
374 | |
375 void* dstRow = dst; | |
376 uint8 srcRow[width * 3]; | |
377 SkAutoTDelete<SkSwizzler> swizzler(SkSwizzler::CreateSwizzler( | |
378 SkSwizzler::kRGB, nullptr, requestedInfo, options)); | |
379 SkASSERT(swizzler); | |
380 for (int i = 0; i < height; ++i) { | |
381 dng_pixel_buffer buffer; | |
382 buffer.fData = &srcRow[0]; | |
383 buffer.fArea = dng_rect(i, 0, i + 1, width); | |
scroggo
2016/01/13 18:24:45
Every setup assignment for buffer is the same for
yujieqin
2016/01/14 09:33:11
Done.
| |
384 buffer.fPlane = 0; | |
385 buffer.fPlanes = 3; | |
386 buffer.fColStep = buffer.fPlanes; | |
387 buffer.fPlaneStep = 1; | |
388 buffer.fPixelType = ttByte; | |
389 buffer.fPixelSize = sizeof(uint8); | |
scroggo
2016/01/13 18:24:45
Should this be uint8_t? (Actually, I just realized
yujieqin
2016/01/14 09:33:11
Done.
| |
390 buffer.fRowStep = sizeof(srcRow); | |
391 | |
392 result = fDngImage->writeToBuffer(&buffer); | |
scroggo
2016/01/13 18:24:45
if (!fDngImage->readRow(&buffer)) {
*rowsDecod
yujieqin
2016/01/14 09:33:11
Done.
| |
393 if (result != kSuccess) { | |
394 return result; | |
395 } | |
396 | |
397 swizzler->swizzle(dstRow, &srcRow[0]); | |
398 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); | |
399 } | |
400 return kSuccess; | |
401 } | |
402 | |
403 SkISize SkRawCodec::onGetScaledDimensions(float desiredScale) const { | |
404 SkASSERT(desiredScale <= 1.f); | |
405 SkISize dim = this->getInfo().dimensions(); | |
406 const int longEdge = SkTMax(dim.fWidth, dim.fHeight); | |
407 | |
408 // Limits the minimun size to be 128 on the long edge. | |
msarett
2016/01/13 17:15:14
This limitation seems reasonable. Is this a limit
scroggo
2016/01/13 18:24:45
I think that's okay. I believe we've had other cas
yujieqin
2016/01/14 09:33:11
Our experiment shows that the DNG SDK does not sca
scroggo
2016/01/14 15:26:38
Can you add a comment explaining that? Also, is it
| |
409 if (desiredScale < 128.f / static_cast<float>(longEdge)) { | |
410 desiredScale = 128.f / static_cast<float>(longEdge); | |
411 } | |
412 | |
413 const float finalScale = (int)(1.f/ desiredScale); | |
414 | |
415 dim.fWidth = std::round(dim.fWidth / finalScale); | |
416 dim.fHeight = std::round(dim.fHeight / finalScale); | |
417 return dim; | |
418 } | |
419 | |
420 bool SkRawCodec::onDimensionsSupported(const SkISize& dim) { | |
421 const SkImageInfo& info = this->getInfo(); | |
422 return dim.width() >= 1 && dim.width() <= info.width() | |
msarett
2016/01/13 17:15:14
Everything below this line is dead code?
yujieqin
2016/01/14 09:33:11
Done.
| |
423 && dim.height() >= 1 && dim.height() <= info.height(); | |
424 | |
425 const SkISize fullDim = this->getInfo().dimensions(); | |
426 const float fullLongEdge = SkTMax(fullDim.fWidth, fullDim.fHeight); | |
427 const float longEdge = SkTMax(dim.fWidth, dim.fHeight); | |
428 | |
429 SkISize sizeFloor = this->onGetScaledDimensions(1.f / std::floor(fullLongEdg e / longEdge)); | |
430 SkISize sizeCeil = this->onGetScaledDimensions(1.f / std::ceil(fullLongEdge / longEdge)); | |
431 return sizeFloor == dim || sizeCeil == dim; | |
432 } | |
433 | |
434 SkRawCodec::~SkRawCodec() {} | |
435 | |
436 SkRawCodec::SkRawCodec(SkDngImage* dngImage) | |
437 : INHERITED(dngImage->getImageInfo(), nullptr) | |
438 , fDngImage(dngImage) {} | |
OLD | NEW |