blob: 4cd44a4df1f8c6300bc9ae3155ce436078b17f8b [file] [log] [blame]
yujieqin916de9f2016-01-25 08:26:16 -08001/*
2 * Copyright 2016 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"
ebrauerb84b5b42016-01-27 08:21:03 -080020#include "SkTaskGroup.h"
yujieqin916de9f2016-01-25 08:26:16 -080021#include "SkTemplates.h"
22#include "SkTypes.h"
23
ebrauerb84b5b42016-01-27 08:21:03 -080024#include "dng_area_task.h"
yujieqin916de9f2016-01-25 08:26:16 -080025#include "dng_color_space.h"
26#include "dng_exceptions.h"
27#include "dng_host.h"
28#include "dng_info.h"
29#include "dng_memory.h"
30#include "dng_render.h"
31#include "dng_stream.h"
32
33#include "src/piex.h"
34
35#include <cmath> // for std::round,floor,ceil
36#include <limits>
37
38namespace {
39
ebrauerb84b5b42016-01-27 08:21:03 -080040// Caluclates the number of tiles of tile_size that fit into the area in vertical and horizontal
41// directions.
42dng_point num_tiles_in_area(const dng_point &areaSize,
43 const dng_point_real64 &tileSize) {
44 // FIXME: Add a ceil_div() helper in SkCodecPriv.h
yujieqinfda27a92016-01-27 09:03:20 -080045 return dng_point(static_cast<int32>((areaSize.v + tileSize.v - 1) / tileSize.v),
46 static_cast<int32>((areaSize.h + tileSize.h - 1) / tileSize.h));
ebrauerb84b5b42016-01-27 08:21:03 -080047}
48
49int num_tasks_required(const dng_point& tilesInTask,
50 const dng_point& tilesInArea) {
51 return ((tilesInArea.v + tilesInTask.v - 1) / tilesInTask.v) *
52 ((tilesInArea.h + tilesInTask.h - 1) / tilesInTask.h);
53}
54
55// Calculate the number of tiles to process per task, taking into account the maximum number of
56// tasks. It prefers to increase horizontally for better locality of reference.
57dng_point num_tiles_per_task(const int maxTasks,
58 const dng_point &tilesInArea) {
59 dng_point tilesInTask = {1, 1};
60 while (num_tasks_required(tilesInTask, tilesInArea) > maxTasks) {
61 if (tilesInTask.h < tilesInArea.h) {
62 ++tilesInTask.h;
63 } else if (tilesInTask.v < tilesInArea.v) {
64 ++tilesInTask.v;
65 } else {
66 ThrowProgramError("num_tiles_per_task calculation is wrong.");
67 }
68 }
69 return tilesInTask;
70}
71
72std::vector<dng_rect> compute_task_areas(const int maxTasks, const dng_rect& area,
73 const dng_point& tileSize) {
74 std::vector<dng_rect> taskAreas;
75 const dng_point tilesInArea = num_tiles_in_area(area.Size(), tileSize);
76 const dng_point tilesPerTask = num_tiles_per_task(maxTasks, tilesInArea);
77 const dng_point taskAreaSize = {tilesPerTask.v * tileSize.v,
78 tilesPerTask.h * tileSize.h};
79 for (int v = 0; v < tilesInArea.v; v += tilesPerTask.v) {
80 for (int h = 0; h < tilesInArea.h; h += tilesPerTask.h) {
81 dng_rect taskArea;
82 taskArea.t = area.t + v * tileSize.v;
83 taskArea.l = area.l + h * tileSize.h;
84 taskArea.b = Min_int32(taskArea.t + taskAreaSize.v, area.b);
85 taskArea.r = Min_int32(taskArea.l + taskAreaSize.h, area.r);
86
87 taskAreas.push_back(taskArea);
88 }
89 }
90 return taskAreas;
91}
92
93class SkDngHost : public dng_host {
94public:
yujieqinfda27a92016-01-27 09:03:20 -080095 explicit SkDngHost(dng_memory_allocator* allocater) : dng_host(allocater) {}
ebrauerb84b5b42016-01-27 08:21:03 -080096
97 void PerformAreaTask(dng_area_task& task, const dng_rect& area) override {
98 // The area task gets split up into max_tasks sub-tasks. The max_tasks is defined by the
99 // dng-sdks default implementation of dng_area_task::MaxThreads() which returns 8 or 32
100 // sub-tasks depending on the architecture.
101 const int maxTasks = static_cast<int>(task.MaxThreads());
102
103 SkTaskGroup taskGroup;
104
105 // tileSize is typically 256x256
106 const dng_point tileSize(task.FindTileSize(area));
107 const std::vector<dng_rect> taskAreas = compute_task_areas(maxTasks, area, tileSize);
yujieqinfda27a92016-01-27 09:03:20 -0800108 const int numTasks = static_cast<int>(taskAreas.size());
ebrauerb84b5b42016-01-27 08:21:03 -0800109
110 task.Start(numTasks, tileSize, &Allocator(), Sniffer());
111 for (int taskIndex = 0; taskIndex < numTasks; ++taskIndex) {
112 taskGroup.add([&task, this, taskIndex, taskAreas, tileSize] {
113 task.ProcessOnThread(taskIndex, taskAreas[taskIndex], tileSize, this->Sniffer());
114 });
115 }
116
117 taskGroup.wait();
118 task.Finish(numTasks);
119 }
120
121 uint32 PerformAreaTaskThreads() override {
122 // FIXME: Need to get the real amount of available threads used in the SkTaskGroup.
123 return kMaxMPThreads;
124 }
125
126private:
127 typedef dng_host INHERITED;
128};
129
yujieqin916de9f2016-01-25 08:26:16 -0800130// T must be unsigned type.
131template <class T>
132bool safe_add_to_size_t(T arg1, T arg2, size_t* result) {
133 SkASSERT(arg1 >= 0);
134 SkASSERT(arg2 >= 0);
135 if (arg1 >= 0 && arg2 <= std::numeric_limits<T>::max() - arg1) {
136 T sum = arg1 + arg2;
137 if (sum <= std::numeric_limits<size_t>::max()) {
138 *result = static_cast<size_t>(sum);
139 return true;
140 }
141 }
142 return false;
143}
144
145class SkDngMemoryAllocator : public dng_memory_allocator {
146public:
147 ~SkDngMemoryAllocator() override {}
148
149 dng_memory_block* Allocate(uint32 size) override {
150 // To avoid arbitary allocation requests which might lead to out-of-memory, limit the
151 // amount of memory that can be allocated at once. The memory limit is based on experiments
152 // and supposed to be sufficient for all valid DNG images.
153 if (size > 300 * 1024 * 1024) { // 300 MB
154 ThrowMemoryFull();
155 }
156 return dng_memory_allocator::Allocate(size);
157 }
158};
159
yujieqin9c7a8a42016-02-05 08:21:19 -0800160bool is_asset_stream(const SkStream& stream) {
161 return stream.hasLength() && stream.hasPosition();
162}
163
yujieqin916de9f2016-01-25 08:26:16 -0800164} // namespace
165
yujieqin9c7a8a42016-02-05 08:21:19 -0800166class SkRawStream {
yujieqin916de9f2016-01-25 08:26:16 -0800167public:
yujieqin9c7a8a42016-02-05 08:21:19 -0800168 virtual ~SkRawStream() {}
yujieqin916de9f2016-01-25 08:26:16 -0800169
yujieqin9c7a8a42016-02-05 08:21:19 -0800170 /*
171 * Gets the length of the stream. Depending on the type of stream, this may require reading to
172 * the end of the stream.
173 */
174 virtual uint64 getLength() = 0;
175
176 virtual bool read(void* data, size_t offset, size_t length) = 0;
yujieqin916de9f2016-01-25 08:26:16 -0800177
178 /*
179 * Creates an SkMemoryStream from the offset with size.
180 * Note: for performance reason, this function is destructive to the SkRawStream. One should
181 * abandon current object after the function call.
182 */
yujieqin9c7a8a42016-02-05 08:21:19 -0800183 virtual SkMemoryStream* transferBuffer(size_t offset, size_t size) = 0;
184};
185
186class SkRawBufferedStream : public SkRawStream {
187public:
188 // Will take the ownership of the stream.
189 explicit SkRawBufferedStream(SkStream* stream)
190 : fStream(stream)
191 , fWholeStreamRead(false)
192 {
193 // Only use SkRawBufferedStream when the stream is not an asset stream.
194 SkASSERT(!is_asset_stream(*stream));
195 }
196
197 ~SkRawBufferedStream() override {}
198
199 uint64 getLength() override {
200 if (!this->bufferMoreData(kReadToEnd)) { // read whole stream
201 ThrowReadFile();
202 }
203 return fStreamBuffer.bytesWritten();
204 }
205
206 bool read(void* data, size_t offset, size_t length) override {
207 if (length == 0) {
208 return true;
209 }
210
211 size_t sum;
212 if (!safe_add_to_size_t(offset, length, &sum)) {
213 return false;
214 }
215
216 return this->bufferMoreData(sum) && fStreamBuffer.read(data, offset, length);
217 }
218
219 SkMemoryStream* transferBuffer(size_t offset, size_t size) override {
yujieqin916de9f2016-01-25 08:26:16 -0800220 SkAutoTUnref<SkData> data(SkData::NewUninitialized(size));
221 if (offset > fStreamBuffer.bytesWritten()) {
222 // If the offset is not buffered, read from fStream directly and skip the buffering.
223 const size_t skipLength = offset - fStreamBuffer.bytesWritten();
224 if (fStream->skip(skipLength) != skipLength) {
225 return nullptr;
226 }
227 const size_t bytesRead = fStream->read(data->writable_data(), size);
228 if (bytesRead < size) {
229 data.reset(SkData::NewSubset(data.get(), 0, bytesRead));
230 }
231 } else {
232 const size_t alreadyBuffered = SkTMin(fStreamBuffer.bytesWritten() - offset, size);
233 if (alreadyBuffered > 0 &&
234 !fStreamBuffer.read(data->writable_data(), offset, alreadyBuffered)) {
235 return nullptr;
236 }
237
238 const size_t remaining = size - alreadyBuffered;
239 if (remaining) {
240 auto* dst = static_cast<uint8_t*>(data->writable_data()) + alreadyBuffered;
241 const size_t bytesRead = fStream->read(dst, remaining);
242 size_t newSize;
243 if (bytesRead < remaining) {
244 if (!safe_add_to_size_t(alreadyBuffered, bytesRead, &newSize)) {
245 return nullptr;
246 }
247 data.reset(SkData::NewSubset(data.get(), 0, newSize));
248 }
249 }
250 }
251 return new SkMemoryStream(data);
252 }
253
yujieqin916de9f2016-01-25 08:26:16 -0800254private:
255 // Note: if the newSize == kReadToEnd (0), this function will read to the end of stream.
256 bool bufferMoreData(size_t newSize) {
257 if (newSize == kReadToEnd) {
258 if (fWholeStreamRead) { // already read-to-end.
259 return true;
260 }
261
262 // TODO: optimize for the special case when the input is SkMemoryStream.
263 return SkStreamCopy(&fStreamBuffer, fStream.get());
264 }
265
266 if (newSize <= fStreamBuffer.bytesWritten()) { // already buffered to newSize
267 return true;
268 }
269 if (fWholeStreamRead) { // newSize is larger than the whole stream.
270 return false;
271 }
272
yujieqin22000d12016-02-02 08:09:07 -0800273 // Try to read at least 8192 bytes to avoid to many small reads.
274 const size_t kMinSizeToRead = 8192;
275 const size_t sizeRequested = newSize - fStreamBuffer.bytesWritten();
276 const size_t sizeToRead = SkTMax(kMinSizeToRead, sizeRequested);
277 SkAutoSTMalloc<kMinSizeToRead, uint8> tempBuffer(sizeToRead);
yujieqin916de9f2016-01-25 08:26:16 -0800278 const size_t bytesRead = fStream->read(tempBuffer.get(), sizeToRead);
yujieqin22000d12016-02-02 08:09:07 -0800279 if (bytesRead < sizeRequested) {
yujieqin916de9f2016-01-25 08:26:16 -0800280 return false;
281 }
282 return fStreamBuffer.write(tempBuffer.get(), bytesRead);
283 }
284
285 SkAutoTDelete<SkStream> fStream;
286 bool fWholeStreamRead;
287
288 SkDynamicMemoryWStream fStreamBuffer;
289
290 const size_t kReadToEnd = 0;
291};
292
yujieqin9c7a8a42016-02-05 08:21:19 -0800293class SkRawAssetStream : public SkRawStream {
yujieqin916de9f2016-01-25 08:26:16 -0800294public:
yujieqin9c7a8a42016-02-05 08:21:19 -0800295 // Will take the ownership of the stream.
296 explicit SkRawAssetStream(SkStream* stream)
297 : fStream(stream)
298 {
299 // Only use SkRawAssetStream when the stream is an asset stream.
300 SkASSERT(is_asset_stream(*stream));
301 }
yujieqin916de9f2016-01-25 08:26:16 -0800302
yujieqin9c7a8a42016-02-05 08:21:19 -0800303 ~SkRawAssetStream() override {}
yujieqin916de9f2016-01-25 08:26:16 -0800304
yujieqin9c7a8a42016-02-05 08:21:19 -0800305 uint64 getLength() override {
306 return fStream->getLength();
307 }
308
309
310 bool read(void* data, size_t offset, size_t length) override {
311 if (length == 0) {
312 return true;
313 }
314
315 size_t sum;
316 if (!safe_add_to_size_t(offset, length, &sum)) {
317 return false;
318 }
319
320 return fStream->seek(offset) && (fStream->read(data, length) == length);
321 }
322
323 SkMemoryStream* transferBuffer(size_t offset, size_t size) override {
324 if (fStream->getLength() < offset) {
325 return nullptr;
326 }
327
328 size_t sum;
329 if (!safe_add_to_size_t(offset, size, &sum)) {
330 return nullptr;
331 }
332
333 // This will allow read less than the requested "size", because the JPEG codec wants to
334 // handle also a partial JPEG file.
335 const size_t bytesToRead = SkTMin(sum, fStream->getLength()) - offset;
336 if (bytesToRead == 0) {
337 return nullptr;
338 }
339
340 if (fStream->getMemoryBase()) { // directly copy if getMemoryBase() is available.
341 SkAutoTUnref<SkData> data(SkData::NewWithCopy(
342 static_cast<const uint8_t*>(fStream->getMemoryBase()) + offset, bytesToRead));
343 fStream.free();
344 return new SkMemoryStream(data);
345 } else {
346 SkAutoTUnref<SkData> data(SkData::NewUninitialized(bytesToRead));
347 if (!fStream->seek(offset)) {
348 return nullptr;
349 }
350 const size_t bytesRead = fStream->read(data->writable_data(), bytesToRead);
351 if (bytesRead < bytesToRead) {
352 data.reset(SkData::NewSubset(data.get(), 0, bytesRead));
353 }
354 return new SkMemoryStream(data);
355 }
356 }
357private:
358 SkAutoTDelete<SkStream> fStream;
359};
360
361class SkPiexStream : public ::piex::StreamInterface {
362public:
363 // Will NOT take the ownership of the stream.
364 explicit SkPiexStream(SkRawStream* stream) : fStream(stream) {}
365
366 ~SkPiexStream() override {}
367
368 ::piex::Error GetData(const size_t offset, const size_t length,
369 uint8* data) override {
370 return fStream->read(static_cast<void*>(data), offset, length) ?
371 ::piex::Error::kOk : ::piex::Error::kFail;
yujieqin916de9f2016-01-25 08:26:16 -0800372 }
373
374private:
yujieqin9c7a8a42016-02-05 08:21:19 -0800375 SkRawStream* fStream;
376};
377
378class SkDngStream : public dng_stream {
379public:
380 // Will NOT take the ownership of the stream.
381 SkDngStream(SkRawStream* stream) : fStream(stream) {}
382
383 ~SkDngStream() override {}
384
385 uint64 DoGetLength() override { return fStream->getLength(); }
386
387 void DoRead(void* data, uint32 count, uint64 offset) override {
388 size_t sum;
389 if (!safe_add_to_size_t(static_cast<uint64>(count), offset, &sum) ||
390 !fStream->read(data, static_cast<size_t>(offset), static_cast<size_t>(count))) {
391 ThrowReadFile();
392 }
393 }
394
395private:
396 SkRawStream* fStream;
yujieqin916de9f2016-01-25 08:26:16 -0800397};
398
399class SkDngImage {
400public:
ebrauer46d2aa82016-02-17 08:04:00 -0800401 /*
402 * Initializes the object with the information from Piex in a first attempt. This way it can
403 * save time and storage to obtain the DNG dimensions and color filter array (CFA) pattern
404 * which is essential for the demosaicing of the sensor image.
405 * Note: this will take the ownership of the stream.
406 */
yujieqin916de9f2016-01-25 08:26:16 -0800407 static SkDngImage* NewFromStream(SkRawStream* stream) {
408 SkAutoTDelete<SkDngImage> dngImage(new SkDngImage(stream));
ebrauer46d2aa82016-02-17 08:04:00 -0800409 if (!dngImage->initFromPiex()) {
410 if (!dngImage->readDng()) {
411 return nullptr;
412 }
yujieqin916de9f2016-01-25 08:26:16 -0800413 }
414
yujieqin916de9f2016-01-25 08:26:16 -0800415 return dngImage.release();
416 }
417
418 /*
419 * Renders the DNG image to the size. The DNG SDK only allows scaling close to integer factors
420 * down to 80 pixels on the short edge. The rendered image will be close to the specified size,
421 * but there is no guarantee that any of the edges will match the requested size. E.g.
422 * 100% size: 4000 x 3000
423 * requested size: 1600 x 1200
424 * returned size could be: 2000 x 1500
425 */
426 dng_image* render(int width, int height) {
427 if (!fHost || !fInfo || !fNegative || !fDngStream) {
428 if (!this->readDng()) {
429 return nullptr;
430 }
431 }
432
433 // render() takes ownership of fHost, fInfo, fNegative and fDngStream when available.
434 SkAutoTDelete<dng_host> host(fHost.release());
435 SkAutoTDelete<dng_info> info(fInfo.release());
436 SkAutoTDelete<dng_negative> negative(fNegative.release());
437 SkAutoTDelete<dng_stream> dngStream(fDngStream.release());
438
439 // DNG SDK preserves the aspect ratio, so it only needs to know the longer dimension.
440 const int preferredSize = SkTMax(width, height);
441 try {
442 host->SetPreferredSize(preferredSize);
443 host->ValidateSizes();
444
445 negative->ReadStage1Image(*host, *dngStream, *info);
446
447 if (info->fMaskIndex != -1) {
448 negative->ReadTransparencyMask(*host, *dngStream, *info);
449 }
450
451 negative->ValidateRawImageDigest(*host);
452 if (negative->IsDamaged()) {
453 return nullptr;
454 }
455
456 const int32 kMosaicPlane = -1;
457 negative->BuildStage2Image(*host);
458 negative->BuildStage3Image(*host, kMosaicPlane);
459
460 dng_render render(*host, *negative);
461 render.SetFinalSpace(dng_space_sRGB::Get());
462 render.SetFinalPixelType(ttByte);
463
464 dng_point stage3_size = negative->Stage3Image()->Size();
465 render.SetMaximumSize(SkTMax(stage3_size.h, stage3_size.v));
466
467 return render.Render();
468 } catch (...) {
469 return nullptr;
470 }
471 }
472
473 const SkImageInfo& getImageInfo() const {
474 return fImageInfo;
475 }
476
477 bool isScalable() const {
478 return fIsScalable;
479 }
480
481 bool isXtransImage() const {
482 return fIsXtransImage;
483 }
484
485private:
ebrauer46d2aa82016-02-17 08:04:00 -0800486 void init(const int width, const int height, const dng_point& cfaPatternSize) {
487 fImageInfo = SkImageInfo::Make(width, height, kN32_SkColorType, kOpaque_SkAlphaType);
488
489 // The DNG SDK scales only during demosaicing, so scaling is only possible when
490 // a mosaic info is available.
491 fIsScalable = cfaPatternSize.v != 0 && cfaPatternSize.h != 0;
492 fIsXtransImage = fIsScalable ? (cfaPatternSize.v == 6 && cfaPatternSize.h == 6) : false;
493 }
494
495 bool initFromPiex() {
496 // Does not take the ownership of rawStream.
497 SkPiexStream piexStream(fStream.get());
498 ::piex::PreviewImageData imageData;
499 if (::piex::IsRaw(&piexStream)
500 && ::piex::GetPreviewImageData(&piexStream, &imageData) == ::piex::Error::kOk)
501 {
502 dng_point cfaPatternSize(imageData.cfa_pattern_dim[1], imageData.cfa_pattern_dim[0]);
503 this->init(static_cast<int>(imageData.full_width),
504 static_cast<int>(imageData.full_height), cfaPatternSize);
505 return true;
506 }
507 return false;
508 }
509
yujieqin916de9f2016-01-25 08:26:16 -0800510 bool readDng() {
511 // Due to the limit of DNG SDK, we need to reset host and info.
ebrauerb84b5b42016-01-27 08:21:03 -0800512 fHost.reset(new SkDngHost(&fAllocator));
yujieqin916de9f2016-01-25 08:26:16 -0800513 fInfo.reset(new dng_info);
514 fDngStream.reset(new SkDngStream(fStream));
515 try {
516 fHost->ValidateSizes();
517 fInfo->Parse(*fHost, *fDngStream);
518 fInfo->PostParse(*fHost);
519 if (!fInfo->IsValidDNG()) {
520 return false;
521 }
522
523 fNegative.reset(fHost->Make_dng_negative());
524 fNegative->Parse(*fHost, *fDngStream, *fInfo);
525 fNegative->PostParse(*fHost, *fDngStream, *fInfo);
526 fNegative->SynchronizeMetadata();
527
ebrauer46d2aa82016-02-17 08:04:00 -0800528 dng_point cfaPatternSize(0, 0);
529 if (fNegative->GetMosaicInfo() != nullptr) {
530 cfaPatternSize = fNegative->GetMosaicInfo()->fCFAPatternSize;
531 }
532 this->init(static_cast<int>(fNegative->DefaultCropSizeH().As_real64()),
533 static_cast<int>(fNegative->DefaultCropSizeV().As_real64()),
534 cfaPatternSize);
yujieqin916de9f2016-01-25 08:26:16 -0800535 return true;
536 } catch (...) {
yujieqin916de9f2016-01-25 08:26:16 -0800537 return false;
538 }
539 }
540
541 SkDngImage(SkRawStream* stream)
542 : fStream(stream) {}
543
544 SkDngMemoryAllocator fAllocator;
545 SkAutoTDelete<SkRawStream> fStream;
546 SkAutoTDelete<dng_host> fHost;
547 SkAutoTDelete<dng_info> fInfo;
548 SkAutoTDelete<dng_negative> fNegative;
549 SkAutoTDelete<dng_stream> fDngStream;
550
551 SkImageInfo fImageInfo;
552 bool fIsScalable;
553 bool fIsXtransImage;
554};
555
556/*
557 * Tries to handle the image with PIEX. If PIEX returns kOk and finds the preview image, create a
558 * SkJpegCodec. If PIEX returns kFail, then the file is invalid, return nullptr. In other cases,
559 * fallback to create SkRawCodec for DNG images.
560 */
561SkCodec* SkRawCodec::NewFromStream(SkStream* stream) {
yujieqin9c7a8a42016-02-05 08:21:19 -0800562 SkAutoTDelete<SkRawStream> rawStream;
563 if (is_asset_stream(*stream)) {
564 rawStream.reset(new SkRawAssetStream(stream));
565 } else {
566 rawStream.reset(new SkRawBufferedStream(stream));
567 }
568
569 // Does not take the ownership of rawStream.
570 SkPiexStream piexStream(rawStream.get());
yujieqin916de9f2016-01-25 08:26:16 -0800571 ::piex::PreviewImageData imageData;
yujieqin9c7a8a42016-02-05 08:21:19 -0800572 if (::piex::IsRaw(&piexStream)) {
573 ::piex::Error error = ::piex::GetPreviewImageData(&piexStream, &imageData);
yujieqin916de9f2016-01-25 08:26:16 -0800574
575 if (error == ::piex::Error::kOk && imageData.preview_length > 0) {
576#if !defined(GOOGLE3)
577 // transferBuffer() is destructive to the rawStream. Abandon the rawStream after this
578 // function call.
579 // FIXME: one may avoid the copy of memoryStream and use the buffered rawStream.
580 SkMemoryStream* memoryStream =
yujieqin9c7a8a42016-02-05 08:21:19 -0800581 rawStream->transferBuffer(imageData.preview_offset, imageData.preview_length);
yujieqin916de9f2016-01-25 08:26:16 -0800582 return memoryStream ? SkJpegCodec::NewFromStream(memoryStream) : nullptr;
583#else
584 return nullptr;
585#endif
586 } else if (error == ::piex::Error::kFail) {
587 return nullptr;
588 }
589 }
590
yujieqin9c7a8a42016-02-05 08:21:19 -0800591 // Takes the ownership of the rawStream.
yujieqin916de9f2016-01-25 08:26:16 -0800592 SkAutoTDelete<SkDngImage> dngImage(SkDngImage::NewFromStream(rawStream.release()));
593 if (!dngImage) {
594 return nullptr;
595 }
596
597 return new SkRawCodec(dngImage.release());
598}
599
600SkCodec::Result SkRawCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst,
601 size_t dstRowBytes, const Options& options,
602 SkPMColor ctable[], int* ctableCount,
603 int* rowsDecoded) {
604 if (!conversion_possible(requestedInfo, this->getInfo())) {
605 SkCodecPrintf("Error: cannot convert input type to output type.\n");
606 return kInvalidConversion;
607 }
608
609 SkAutoTDelete<SkSwizzler> swizzler(SkSwizzler::CreateSwizzler(
610 SkSwizzler::kRGB, nullptr, requestedInfo, options));
611 SkASSERT(swizzler);
612
613 const int width = requestedInfo.width();
614 const int height = requestedInfo.height();
615 SkAutoTDelete<dng_image> image(fDngImage->render(width, height));
616 if (!image) {
617 return kInvalidInput;
618 }
619
620 // Because the DNG SDK can not guarantee to render to requested size, we allow a small
621 // difference. Only the overlapping region will be converted.
622 const float maxDiffRatio = 1.03f;
623 const dng_point& imageSize = image->Size();
624 if (imageSize.h / width > maxDiffRatio || imageSize.h < width ||
625 imageSize.v / height > maxDiffRatio || imageSize.v < height) {
626 return SkCodec::kInvalidScale;
627 }
628
629 void* dstRow = dst;
yujieqin24716be2016-01-27 07:59:00 -0800630 SkAutoTMalloc<uint8_t> srcRow(width * 3);
yujieqin916de9f2016-01-25 08:26:16 -0800631
632 dng_pixel_buffer buffer;
633 buffer.fData = &srcRow[0];
634 buffer.fPlane = 0;
635 buffer.fPlanes = 3;
636 buffer.fColStep = buffer.fPlanes;
637 buffer.fPlaneStep = 1;
638 buffer.fPixelType = ttByte;
639 buffer.fPixelSize = sizeof(uint8_t);
scroggoe6459652016-01-30 10:06:11 -0800640 buffer.fRowStep = width * 3;
yujieqin916de9f2016-01-25 08:26:16 -0800641
642 for (int i = 0; i < height; ++i) {
643 buffer.fArea = dng_rect(i, 0, i + 1, width);
644
645 try {
646 image->Get(buffer, dng_image::edge_zero);
647 } catch (...) {
648 *rowsDecoded = i;
649 return kIncompleteInput;
650 }
651
652 swizzler->swizzle(dstRow, &srcRow[0]);
653 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
654 }
655 return kSuccess;
656}
657
658SkISize SkRawCodec::onGetScaledDimensions(float desiredScale) const {
659 SkASSERT(desiredScale <= 1.f);
660 const SkISize dim = this->getInfo().dimensions();
661 if (!fDngImage->isScalable()) {
662 return dim;
663 }
664
665 // Limits the minimum size to be 80 on the short edge.
yujieqin076d83d2016-01-27 08:25:53 -0800666 const float shortEdge = static_cast<float>(SkTMin(dim.fWidth, dim.fHeight));
yujieqin916de9f2016-01-25 08:26:16 -0800667 if (desiredScale < 80.f / shortEdge) {
668 desiredScale = 80.f / shortEdge;
669 }
670
671 // For Xtrans images, the integer-factor scaling does not support the half-size scaling case
672 // (stronger downscalings are fine). In this case, returns the factor "3" scaling instead.
673 if (fDngImage->isXtransImage() && desiredScale > 1.f / 3.f && desiredScale < 1.f) {
674 desiredScale = 1.f / 3.f;
675 }
676
677 // Round to integer-factors.
678 const float finalScale = std::floor(1.f/ desiredScale);
yujieqin076d83d2016-01-27 08:25:53 -0800679 return SkISize::Make(static_cast<int32_t>(std::floor(dim.fWidth / finalScale)),
680 static_cast<int32_t>(std::floor(dim.fHeight / finalScale)));
yujieqin916de9f2016-01-25 08:26:16 -0800681}
682
683bool SkRawCodec::onDimensionsSupported(const SkISize& dim) {
684 const SkISize fullDim = this->getInfo().dimensions();
yujieqin076d83d2016-01-27 08:25:53 -0800685 const float fullShortEdge = static_cast<float>(SkTMin(fullDim.fWidth, fullDim.fHeight));
686 const float shortEdge = static_cast<float>(SkTMin(dim.fWidth, dim.fHeight));
yujieqin916de9f2016-01-25 08:26:16 -0800687
688 SkISize sizeFloor = this->onGetScaledDimensions(1.f / std::floor(fullShortEdge / shortEdge));
689 SkISize sizeCeil = this->onGetScaledDimensions(1.f / std::ceil(fullShortEdge / shortEdge));
690 return sizeFloor == dim || sizeCeil == dim;
691}
692
693SkRawCodec::~SkRawCodec() {}
694
695SkRawCodec::SkRawCodec(SkDngImage* dngImage)
696 : INHERITED(dngImage->getImageInfo(), nullptr)
697 , fDngImage(dngImage) {}