blob: c7de3172863876cdbf1ec0e2d5a2d0111e6ddc3f [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
160} // namespace
161
162// Note: this class could throw exception if it is used as dng_stream.
163class SkRawStream : public ::piex::StreamInterface {
164public:
165 // Note that this call will take the ownership of stream.
166 explicit SkRawStream(SkStream* stream)
167 : fStream(stream), fWholeStreamRead(false) {}
168
169 ~SkRawStream() override {}
170
171 /*
172 * Creates an SkMemoryStream from the offset with size.
173 * Note: for performance reason, this function is destructive to the SkRawStream. One should
174 * abandon current object after the function call.
175 */
176 SkMemoryStream* transferBuffer(size_t offset, size_t size) {
177 SkAutoTUnref<SkData> data(SkData::NewUninitialized(size));
178 if (offset > fStreamBuffer.bytesWritten()) {
179 // If the offset is not buffered, read from fStream directly and skip the buffering.
180 const size_t skipLength = offset - fStreamBuffer.bytesWritten();
181 if (fStream->skip(skipLength) != skipLength) {
182 return nullptr;
183 }
184 const size_t bytesRead = fStream->read(data->writable_data(), size);
185 if (bytesRead < size) {
186 data.reset(SkData::NewSubset(data.get(), 0, bytesRead));
187 }
188 } else {
189 const size_t alreadyBuffered = SkTMin(fStreamBuffer.bytesWritten() - offset, size);
190 if (alreadyBuffered > 0 &&
191 !fStreamBuffer.read(data->writable_data(), offset, alreadyBuffered)) {
192 return nullptr;
193 }
194
195 const size_t remaining = size - alreadyBuffered;
196 if (remaining) {
197 auto* dst = static_cast<uint8_t*>(data->writable_data()) + alreadyBuffered;
198 const size_t bytesRead = fStream->read(dst, remaining);
199 size_t newSize;
200 if (bytesRead < remaining) {
201 if (!safe_add_to_size_t(alreadyBuffered, bytesRead, &newSize)) {
202 return nullptr;
203 }
204 data.reset(SkData::NewSubset(data.get(), 0, newSize));
205 }
206 }
207 }
208 return new SkMemoryStream(data);
209 }
210
211 // For PIEX
212 ::piex::Error GetData(const size_t offset, const size_t length,
213 uint8* data) override {
214 if (offset == 0 && length == 0) {
215 return ::piex::Error::kOk;
216 }
217 size_t sum;
218 if (!safe_add_to_size_t(offset, length, &sum) || !this->bufferMoreData(sum)) {
219 return ::piex::Error::kFail;
220 }
221 if (!fStreamBuffer.read(data, offset, length)) {
222 return ::piex::Error::kFail;
223 }
224 return ::piex::Error::kOk;
225 }
226
227 // For dng_stream
228 uint64 getLength() {
229 if (!this->bufferMoreData(kReadToEnd)) { // read whole stream
230 ThrowReadFile();
231 }
232 return fStreamBuffer.bytesWritten();
233 }
234
235 // For dng_stream
236 void read(void* data, uint32 count, uint64 offset) {
237 if (count == 0 && offset == 0) {
238 return;
239 }
240 size_t sum;
241 if (!safe_add_to_size_t(static_cast<uint64>(count), offset, &sum) ||
242 !this->bufferMoreData(sum)) {
243 ThrowReadFile();
244 }
245
yujieqin076d83d2016-01-27 08:25:53 -0800246 if (!fStreamBuffer.read(data, static_cast<size_t>(offset), count)) {
yujieqin916de9f2016-01-25 08:26:16 -0800247 ThrowReadFile();
248 }
249 }
250
251private:
252 // Note: if the newSize == kReadToEnd (0), this function will read to the end of stream.
253 bool bufferMoreData(size_t newSize) {
254 if (newSize == kReadToEnd) {
255 if (fWholeStreamRead) { // already read-to-end.
256 return true;
257 }
258
259 // TODO: optimize for the special case when the input is SkMemoryStream.
260 return SkStreamCopy(&fStreamBuffer, fStream.get());
261 }
262
263 if (newSize <= fStreamBuffer.bytesWritten()) { // already buffered to newSize
264 return true;
265 }
266 if (fWholeStreamRead) { // newSize is larger than the whole stream.
267 return false;
268 }
269
yujieqin22000d12016-02-02 08:09:07 -0800270 // Try to read at least 8192 bytes to avoid to many small reads.
271 const size_t kMinSizeToRead = 8192;
272 const size_t sizeRequested = newSize - fStreamBuffer.bytesWritten();
273 const size_t sizeToRead = SkTMax(kMinSizeToRead, sizeRequested);
274 SkAutoSTMalloc<kMinSizeToRead, uint8> tempBuffer(sizeToRead);
yujieqin916de9f2016-01-25 08:26:16 -0800275 const size_t bytesRead = fStream->read(tempBuffer.get(), sizeToRead);
yujieqin22000d12016-02-02 08:09:07 -0800276 if (bytesRead < sizeRequested) {
yujieqin916de9f2016-01-25 08:26:16 -0800277 return false;
278 }
279 return fStreamBuffer.write(tempBuffer.get(), bytesRead);
280 }
281
282 SkAutoTDelete<SkStream> fStream;
283 bool fWholeStreamRead;
284
285 SkDynamicMemoryWStream fStreamBuffer;
286
287 const size_t kReadToEnd = 0;
288};
289
290class SkDngStream : public dng_stream {
291public:
292 SkDngStream(SkRawStream* rawStream) : fRawStream(rawStream) {}
293
294 uint64 DoGetLength() override { return fRawStream->getLength(); }
295
296 void DoRead(void* data, uint32 count, uint64 offset) override {
297 fRawStream->read(data, count, offset);
298 }
299
300private:
301 SkRawStream* fRawStream;
302};
303
304class SkDngImage {
305public:
306 static SkDngImage* NewFromStream(SkRawStream* stream) {
307 SkAutoTDelete<SkDngImage> dngImage(new SkDngImage(stream));
308 if (!dngImage->readDng()) {
309 return nullptr;
310 }
311
312 SkASSERT(dngImage->fNegative);
313 return dngImage.release();
314 }
315
316 /*
317 * Renders the DNG image to the size. The DNG SDK only allows scaling close to integer factors
318 * down to 80 pixels on the short edge. The rendered image will be close to the specified size,
319 * but there is no guarantee that any of the edges will match the requested size. E.g.
320 * 100% size: 4000 x 3000
321 * requested size: 1600 x 1200
322 * returned size could be: 2000 x 1500
323 */
324 dng_image* render(int width, int height) {
325 if (!fHost || !fInfo || !fNegative || !fDngStream) {
326 if (!this->readDng()) {
327 return nullptr;
328 }
329 }
330
331 // render() takes ownership of fHost, fInfo, fNegative and fDngStream when available.
332 SkAutoTDelete<dng_host> host(fHost.release());
333 SkAutoTDelete<dng_info> info(fInfo.release());
334 SkAutoTDelete<dng_negative> negative(fNegative.release());
335 SkAutoTDelete<dng_stream> dngStream(fDngStream.release());
336
337 // DNG SDK preserves the aspect ratio, so it only needs to know the longer dimension.
338 const int preferredSize = SkTMax(width, height);
339 try {
340 host->SetPreferredSize(preferredSize);
341 host->ValidateSizes();
342
343 negative->ReadStage1Image(*host, *dngStream, *info);
344
345 if (info->fMaskIndex != -1) {
346 negative->ReadTransparencyMask(*host, *dngStream, *info);
347 }
348
349 negative->ValidateRawImageDigest(*host);
350 if (negative->IsDamaged()) {
351 return nullptr;
352 }
353
354 const int32 kMosaicPlane = -1;
355 negative->BuildStage2Image(*host);
356 negative->BuildStage3Image(*host, kMosaicPlane);
357
358 dng_render render(*host, *negative);
359 render.SetFinalSpace(dng_space_sRGB::Get());
360 render.SetFinalPixelType(ttByte);
361
362 dng_point stage3_size = negative->Stage3Image()->Size();
363 render.SetMaximumSize(SkTMax(stage3_size.h, stage3_size.v));
364
365 return render.Render();
366 } catch (...) {
367 return nullptr;
368 }
369 }
370
371 const SkImageInfo& getImageInfo() const {
372 return fImageInfo;
373 }
374
375 bool isScalable() const {
376 return fIsScalable;
377 }
378
379 bool isXtransImage() const {
380 return fIsXtransImage;
381 }
382
383private:
384 bool readDng() {
385 // Due to the limit of DNG SDK, we need to reset host and info.
ebrauerb84b5b42016-01-27 08:21:03 -0800386 fHost.reset(new SkDngHost(&fAllocator));
yujieqin916de9f2016-01-25 08:26:16 -0800387 fInfo.reset(new dng_info);
388 fDngStream.reset(new SkDngStream(fStream));
389 try {
390 fHost->ValidateSizes();
391 fInfo->Parse(*fHost, *fDngStream);
392 fInfo->PostParse(*fHost);
393 if (!fInfo->IsValidDNG()) {
394 return false;
395 }
396
397 fNegative.reset(fHost->Make_dng_negative());
398 fNegative->Parse(*fHost, *fDngStream, *fInfo);
399 fNegative->PostParse(*fHost, *fDngStream, *fInfo);
400 fNegative->SynchronizeMetadata();
401
yujieqin076d83d2016-01-27 08:25:53 -0800402 fImageInfo = SkImageInfo::Make(
403 static_cast<int>(fNegative->DefaultCropSizeH().As_real64()),
404 static_cast<int>(fNegative->DefaultCropSizeV().As_real64()),
405 kN32_SkColorType, kOpaque_SkAlphaType);
yujieqin916de9f2016-01-25 08:26:16 -0800406
407 // The DNG SDK scales only for at demosaicing, so only when a mosaic info
408 // is available also scale is available.
409 fIsScalable = fNegative->GetMosaicInfo() != nullptr;
410 fIsXtransImage = fIsScalable
411 ? (fNegative->GetMosaicInfo()->fCFAPatternSize.v == 6
412 && fNegative->GetMosaicInfo()->fCFAPatternSize.h == 6)
413 : false;
414 return true;
415 } catch (...) {
416 fNegative.reset(nullptr);
417 return false;
418 }
419 }
420
421 SkDngImage(SkRawStream* stream)
422 : fStream(stream) {}
423
424 SkDngMemoryAllocator fAllocator;
425 SkAutoTDelete<SkRawStream> fStream;
426 SkAutoTDelete<dng_host> fHost;
427 SkAutoTDelete<dng_info> fInfo;
428 SkAutoTDelete<dng_negative> fNegative;
429 SkAutoTDelete<dng_stream> fDngStream;
430
431 SkImageInfo fImageInfo;
432 bool fIsScalable;
433 bool fIsXtransImage;
434};
435
436/*
437 * Tries to handle the image with PIEX. If PIEX returns kOk and finds the preview image, create a
438 * SkJpegCodec. If PIEX returns kFail, then the file is invalid, return nullptr. In other cases,
439 * fallback to create SkRawCodec for DNG images.
440 */
441SkCodec* SkRawCodec::NewFromStream(SkStream* stream) {
442 SkAutoTDelete<SkRawStream> rawStream(new SkRawStream(stream));
443 ::piex::PreviewImageData imageData;
yujieqin916de9f2016-01-25 08:26:16 -0800444 if (::piex::IsRaw(rawStream.get())) {
445 ::piex::Error error = ::piex::GetPreviewImageData(rawStream.get(), &imageData);
446
447 if (error == ::piex::Error::kOk && imageData.preview_length > 0) {
448#if !defined(GOOGLE3)
449 // transferBuffer() is destructive to the rawStream. Abandon the rawStream after this
450 // function call.
451 // FIXME: one may avoid the copy of memoryStream and use the buffered rawStream.
452 SkMemoryStream* memoryStream =
453 rawStream->transferBuffer(imageData.preview_offset, imageData.preview_length);
454 return memoryStream ? SkJpegCodec::NewFromStream(memoryStream) : nullptr;
455#else
456 return nullptr;
457#endif
458 } else if (error == ::piex::Error::kFail) {
459 return nullptr;
460 }
461 }
462
463 SkAutoTDelete<SkDngImage> dngImage(SkDngImage::NewFromStream(rawStream.release()));
464 if (!dngImage) {
465 return nullptr;
466 }
467
468 return new SkRawCodec(dngImage.release());
469}
470
471SkCodec::Result SkRawCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst,
472 size_t dstRowBytes, const Options& options,
473 SkPMColor ctable[], int* ctableCount,
474 int* rowsDecoded) {
475 if (!conversion_possible(requestedInfo, this->getInfo())) {
476 SkCodecPrintf("Error: cannot convert input type to output type.\n");
477 return kInvalidConversion;
478 }
479
480 SkAutoTDelete<SkSwizzler> swizzler(SkSwizzler::CreateSwizzler(
481 SkSwizzler::kRGB, nullptr, requestedInfo, options));
482 SkASSERT(swizzler);
483
484 const int width = requestedInfo.width();
485 const int height = requestedInfo.height();
486 SkAutoTDelete<dng_image> image(fDngImage->render(width, height));
487 if (!image) {
488 return kInvalidInput;
489 }
490
491 // Because the DNG SDK can not guarantee to render to requested size, we allow a small
492 // difference. Only the overlapping region will be converted.
493 const float maxDiffRatio = 1.03f;
494 const dng_point& imageSize = image->Size();
495 if (imageSize.h / width > maxDiffRatio || imageSize.h < width ||
496 imageSize.v / height > maxDiffRatio || imageSize.v < height) {
497 return SkCodec::kInvalidScale;
498 }
499
500 void* dstRow = dst;
yujieqin24716be2016-01-27 07:59:00 -0800501 SkAutoTMalloc<uint8_t> srcRow(width * 3);
yujieqin916de9f2016-01-25 08:26:16 -0800502
503 dng_pixel_buffer buffer;
504 buffer.fData = &srcRow[0];
505 buffer.fPlane = 0;
506 buffer.fPlanes = 3;
507 buffer.fColStep = buffer.fPlanes;
508 buffer.fPlaneStep = 1;
509 buffer.fPixelType = ttByte;
510 buffer.fPixelSize = sizeof(uint8_t);
scroggoe6459652016-01-30 10:06:11 -0800511 buffer.fRowStep = width * 3;
yujieqin916de9f2016-01-25 08:26:16 -0800512
513 for (int i = 0; i < height; ++i) {
514 buffer.fArea = dng_rect(i, 0, i + 1, width);
515
516 try {
517 image->Get(buffer, dng_image::edge_zero);
518 } catch (...) {
519 *rowsDecoded = i;
520 return kIncompleteInput;
521 }
522
523 swizzler->swizzle(dstRow, &srcRow[0]);
524 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
525 }
526 return kSuccess;
527}
528
529SkISize SkRawCodec::onGetScaledDimensions(float desiredScale) const {
530 SkASSERT(desiredScale <= 1.f);
531 const SkISize dim = this->getInfo().dimensions();
532 if (!fDngImage->isScalable()) {
533 return dim;
534 }
535
536 // Limits the minimum size to be 80 on the short edge.
yujieqin076d83d2016-01-27 08:25:53 -0800537 const float shortEdge = static_cast<float>(SkTMin(dim.fWidth, dim.fHeight));
yujieqin916de9f2016-01-25 08:26:16 -0800538 if (desiredScale < 80.f / shortEdge) {
539 desiredScale = 80.f / shortEdge;
540 }
541
542 // For Xtrans images, the integer-factor scaling does not support the half-size scaling case
543 // (stronger downscalings are fine). In this case, returns the factor "3" scaling instead.
544 if (fDngImage->isXtransImage() && desiredScale > 1.f / 3.f && desiredScale < 1.f) {
545 desiredScale = 1.f / 3.f;
546 }
547
548 // Round to integer-factors.
549 const float finalScale = std::floor(1.f/ desiredScale);
yujieqin076d83d2016-01-27 08:25:53 -0800550 return SkISize::Make(static_cast<int32_t>(std::floor(dim.fWidth / finalScale)),
551 static_cast<int32_t>(std::floor(dim.fHeight / finalScale)));
yujieqin916de9f2016-01-25 08:26:16 -0800552}
553
554bool SkRawCodec::onDimensionsSupported(const SkISize& dim) {
555 const SkISize fullDim = this->getInfo().dimensions();
yujieqin076d83d2016-01-27 08:25:53 -0800556 const float fullShortEdge = static_cast<float>(SkTMin(fullDim.fWidth, fullDim.fHeight));
557 const float shortEdge = static_cast<float>(SkTMin(dim.fWidth, dim.fHeight));
yujieqin916de9f2016-01-25 08:26:16 -0800558
559 SkISize sizeFloor = this->onGetScaledDimensions(1.f / std::floor(fullShortEdge / shortEdge));
560 SkISize sizeCeil = this->onGetScaledDimensions(1.f / std::ceil(fullShortEdge / shortEdge));
561 return sizeFloor == dim || sizeCeil == dim;
562}
563
564SkRawCodec::~SkRawCodec() {}
565
566SkRawCodec::SkRawCodec(SkDngImage* dngImage)
567 : INHERITED(dngImage->getImageInfo(), nullptr)
568 , fDngImage(dngImage) {}