blob: 59034272fab0d89574614a65f54d221fadf96727 [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
45 return dng_point((areaSize.v + tileSize.v - 1) / tileSize.v,
46 (areaSize.h + tileSize.h - 1) / tileSize.h);
47}
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:
95 using dng_host::dng_host;
96
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);
108 const int numTasks = taskAreas.size();
109
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
246 if (!fStreamBuffer.read(data, offset, count)) {
247 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
270 const size_t sizeToRead = newSize - fStreamBuffer.bytesWritten();
271 SkAutoTMalloc<uint8> tempBuffer(sizeToRead);
272 const size_t bytesRead = fStream->read(tempBuffer.get(), sizeToRead);
273 if (bytesRead != sizeToRead) {
274 return false;
275 }
276 return fStreamBuffer.write(tempBuffer.get(), bytesRead);
277 }
278
279 SkAutoTDelete<SkStream> fStream;
280 bool fWholeStreamRead;
281
282 SkDynamicMemoryWStream fStreamBuffer;
283
284 const size_t kReadToEnd = 0;
285};
286
287class SkDngStream : public dng_stream {
288public:
289 SkDngStream(SkRawStream* rawStream) : fRawStream(rawStream) {}
290
291 uint64 DoGetLength() override { return fRawStream->getLength(); }
292
293 void DoRead(void* data, uint32 count, uint64 offset) override {
294 fRawStream->read(data, count, offset);
295 }
296
297private:
298 SkRawStream* fRawStream;
299};
300
301class SkDngImage {
302public:
303 static SkDngImage* NewFromStream(SkRawStream* stream) {
304 SkAutoTDelete<SkDngImage> dngImage(new SkDngImage(stream));
305 if (!dngImage->readDng()) {
306 return nullptr;
307 }
308
309 SkASSERT(dngImage->fNegative);
310 return dngImage.release();
311 }
312
313 /*
314 * Renders the DNG image to the size. The DNG SDK only allows scaling close to integer factors
315 * down to 80 pixels on the short edge. The rendered image will be close to the specified size,
316 * but there is no guarantee that any of the edges will match the requested size. E.g.
317 * 100% size: 4000 x 3000
318 * requested size: 1600 x 1200
319 * returned size could be: 2000 x 1500
320 */
321 dng_image* render(int width, int height) {
322 if (!fHost || !fInfo || !fNegative || !fDngStream) {
323 if (!this->readDng()) {
324 return nullptr;
325 }
326 }
327
328 // render() takes ownership of fHost, fInfo, fNegative and fDngStream when available.
329 SkAutoTDelete<dng_host> host(fHost.release());
330 SkAutoTDelete<dng_info> info(fInfo.release());
331 SkAutoTDelete<dng_negative> negative(fNegative.release());
332 SkAutoTDelete<dng_stream> dngStream(fDngStream.release());
333
334 // DNG SDK preserves the aspect ratio, so it only needs to know the longer dimension.
335 const int preferredSize = SkTMax(width, height);
336 try {
337 host->SetPreferredSize(preferredSize);
338 host->ValidateSizes();
339
340 negative->ReadStage1Image(*host, *dngStream, *info);
341
342 if (info->fMaskIndex != -1) {
343 negative->ReadTransparencyMask(*host, *dngStream, *info);
344 }
345
346 negative->ValidateRawImageDigest(*host);
347 if (negative->IsDamaged()) {
348 return nullptr;
349 }
350
351 const int32 kMosaicPlane = -1;
352 negative->BuildStage2Image(*host);
353 negative->BuildStage3Image(*host, kMosaicPlane);
354
355 dng_render render(*host, *negative);
356 render.SetFinalSpace(dng_space_sRGB::Get());
357 render.SetFinalPixelType(ttByte);
358
359 dng_point stage3_size = negative->Stage3Image()->Size();
360 render.SetMaximumSize(SkTMax(stage3_size.h, stage3_size.v));
361
362 return render.Render();
363 } catch (...) {
364 return nullptr;
365 }
366 }
367
368 const SkImageInfo& getImageInfo() const {
369 return fImageInfo;
370 }
371
372 bool isScalable() const {
373 return fIsScalable;
374 }
375
376 bool isXtransImage() const {
377 return fIsXtransImage;
378 }
379
380private:
381 bool readDng() {
382 // Due to the limit of DNG SDK, we need to reset host and info.
ebrauerb84b5b42016-01-27 08:21:03 -0800383 fHost.reset(new SkDngHost(&fAllocator));
yujieqin916de9f2016-01-25 08:26:16 -0800384 fInfo.reset(new dng_info);
385 fDngStream.reset(new SkDngStream(fStream));
386 try {
387 fHost->ValidateSizes();
388 fInfo->Parse(*fHost, *fDngStream);
389 fInfo->PostParse(*fHost);
390 if (!fInfo->IsValidDNG()) {
391 return false;
392 }
393
394 fNegative.reset(fHost->Make_dng_negative());
395 fNegative->Parse(*fHost, *fDngStream, *fInfo);
396 fNegative->PostParse(*fHost, *fDngStream, *fInfo);
397 fNegative->SynchronizeMetadata();
398
399 fImageInfo = SkImageInfo::Make(fNegative->DefaultCropSizeH().As_real64(),
400 fNegative->DefaultCropSizeV().As_real64(),
401 kN32_SkColorType, kOpaque_SkAlphaType);
402
403 // The DNG SDK scales only for at demosaicing, so only when a mosaic info
404 // is available also scale is available.
405 fIsScalable = fNegative->GetMosaicInfo() != nullptr;
406 fIsXtransImage = fIsScalable
407 ? (fNegative->GetMosaicInfo()->fCFAPatternSize.v == 6
408 && fNegative->GetMosaicInfo()->fCFAPatternSize.h == 6)
409 : false;
410 return true;
411 } catch (...) {
412 fNegative.reset(nullptr);
413 return false;
414 }
415 }
416
417 SkDngImage(SkRawStream* stream)
418 : fStream(stream) {}
419
420 SkDngMemoryAllocator fAllocator;
421 SkAutoTDelete<SkRawStream> fStream;
422 SkAutoTDelete<dng_host> fHost;
423 SkAutoTDelete<dng_info> fInfo;
424 SkAutoTDelete<dng_negative> fNegative;
425 SkAutoTDelete<dng_stream> fDngStream;
426
427 SkImageInfo fImageInfo;
428 bool fIsScalable;
429 bool fIsXtransImage;
430};
431
432/*
433 * Tries to handle the image with PIEX. If PIEX returns kOk and finds the preview image, create a
434 * SkJpegCodec. If PIEX returns kFail, then the file is invalid, return nullptr. In other cases,
435 * fallback to create SkRawCodec for DNG images.
436 */
437SkCodec* SkRawCodec::NewFromStream(SkStream* stream) {
438 SkAutoTDelete<SkRawStream> rawStream(new SkRawStream(stream));
439 ::piex::PreviewImageData imageData;
440 // FIXME: ::piex::GetPreviewImageData() calls GetData() frequently with small amounts,
441 // resulting in many calls to bufferMoreData(). Could we make this more efficient by grouping
442 // smaller requests together?
443 if (::piex::IsRaw(rawStream.get())) {
444 ::piex::Error error = ::piex::GetPreviewImageData(rawStream.get(), &imageData);
445
446 if (error == ::piex::Error::kOk && imageData.preview_length > 0) {
447#if !defined(GOOGLE3)
448 // transferBuffer() is destructive to the rawStream. Abandon the rawStream after this
449 // function call.
450 // FIXME: one may avoid the copy of memoryStream and use the buffered rawStream.
451 SkMemoryStream* memoryStream =
452 rawStream->transferBuffer(imageData.preview_offset, imageData.preview_length);
453 return memoryStream ? SkJpegCodec::NewFromStream(memoryStream) : nullptr;
454#else
455 return nullptr;
456#endif
457 } else if (error == ::piex::Error::kFail) {
458 return nullptr;
459 }
460 }
461
462 SkAutoTDelete<SkDngImage> dngImage(SkDngImage::NewFromStream(rawStream.release()));
463 if (!dngImage) {
464 return nullptr;
465 }
466
467 return new SkRawCodec(dngImage.release());
468}
469
470SkCodec::Result SkRawCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst,
471 size_t dstRowBytes, const Options& options,
472 SkPMColor ctable[], int* ctableCount,
473 int* rowsDecoded) {
474 if (!conversion_possible(requestedInfo, this->getInfo())) {
475 SkCodecPrintf("Error: cannot convert input type to output type.\n");
476 return kInvalidConversion;
477 }
478
479 SkAutoTDelete<SkSwizzler> swizzler(SkSwizzler::CreateSwizzler(
480 SkSwizzler::kRGB, nullptr, requestedInfo, options));
481 SkASSERT(swizzler);
482
483 const int width = requestedInfo.width();
484 const int height = requestedInfo.height();
485 SkAutoTDelete<dng_image> image(fDngImage->render(width, height));
486 if (!image) {
487 return kInvalidInput;
488 }
489
490 // Because the DNG SDK can not guarantee to render to requested size, we allow a small
491 // difference. Only the overlapping region will be converted.
492 const float maxDiffRatio = 1.03f;
493 const dng_point& imageSize = image->Size();
494 if (imageSize.h / width > maxDiffRatio || imageSize.h < width ||
495 imageSize.v / height > maxDiffRatio || imageSize.v < height) {
496 return SkCodec::kInvalidScale;
497 }
498
499 void* dstRow = dst;
yujieqin24716be2016-01-27 07:59:00 -0800500 SkAutoTMalloc<uint8_t> srcRow(width * 3);
yujieqin916de9f2016-01-25 08:26:16 -0800501
502 dng_pixel_buffer buffer;
503 buffer.fData = &srcRow[0];
504 buffer.fPlane = 0;
505 buffer.fPlanes = 3;
506 buffer.fColStep = buffer.fPlanes;
507 buffer.fPlaneStep = 1;
508 buffer.fPixelType = ttByte;
509 buffer.fPixelSize = sizeof(uint8_t);
510 buffer.fRowStep = sizeof(srcRow);
511
512 for (int i = 0; i < height; ++i) {
513 buffer.fArea = dng_rect(i, 0, i + 1, width);
514
515 try {
516 image->Get(buffer, dng_image::edge_zero);
517 } catch (...) {
518 *rowsDecoded = i;
519 return kIncompleteInput;
520 }
521
522 swizzler->swizzle(dstRow, &srcRow[0]);
523 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
524 }
525 return kSuccess;
526}
527
528SkISize SkRawCodec::onGetScaledDimensions(float desiredScale) const {
529 SkASSERT(desiredScale <= 1.f);
530 const SkISize dim = this->getInfo().dimensions();
531 if (!fDngImage->isScalable()) {
532 return dim;
533 }
534
535 // Limits the minimum size to be 80 on the short edge.
536 const float shortEdge = SkTMin(dim.fWidth, dim.fHeight);
537 if (desiredScale < 80.f / shortEdge) {
538 desiredScale = 80.f / shortEdge;
539 }
540
541 // For Xtrans images, the integer-factor scaling does not support the half-size scaling case
542 // (stronger downscalings are fine). In this case, returns the factor "3" scaling instead.
543 if (fDngImage->isXtransImage() && desiredScale > 1.f / 3.f && desiredScale < 1.f) {
544 desiredScale = 1.f / 3.f;
545 }
546
547 // Round to integer-factors.
548 const float finalScale = std::floor(1.f/ desiredScale);
549 return SkISize::Make(std::floor(dim.fWidth / finalScale),
550 std::floor(dim.fHeight / finalScale));
551}
552
553bool SkRawCodec::onDimensionsSupported(const SkISize& dim) {
554 const SkISize fullDim = this->getInfo().dimensions();
555 const float fullShortEdge = SkTMin(fullDim.fWidth, fullDim.fHeight);
556 const float shortEdge = SkTMin(dim.fWidth, dim.fHeight);
557
558 SkISize sizeFloor = this->onGetScaledDimensions(1.f / std::floor(fullShortEdge / shortEdge));
559 SkISize sizeCeil = this->onGetScaledDimensions(1.f / std::ceil(fullShortEdge / shortEdge));
560 return sizeFloor == dim || sizeCeil == dim;
561}
562
563SkRawCodec::~SkRawCodec() {}
564
565SkRawCodec::SkRawCodec(SkDngImage* dngImage)
566 : INHERITED(dngImage->getImageInfo(), nullptr)
567 , fDngImage(dngImage) {}