Index: src/images/SkEXRImageEncoder.cpp |
diff --git a/src/images/SkEXRImageEncoder.cpp b/src/images/SkEXRImageEncoder.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..416e1322fd7c5683bf35b2ce23e4e05f20dd37d8 |
--- /dev/null |
+++ b/src/images/SkEXRImageEncoder.cpp |
@@ -0,0 +1,134 @@ |
+/* |
+ * Copyright 2016 Google Inc. |
+ * |
+ * Use of this source code is governed by a BSD-style license that can be |
+ * found in the LICENSE file. |
+ */ |
+ |
+#include "SkBitmap.h" |
+#include "SkImageEncoder.h" |
+#include "SkHalf.h" |
+#include "SkStream.h" |
+#include "SkTemplates.h" |
+ |
+#define TINYEXR_IMPLEMENTATION |
+#include "tinyexr.h" |
+ |
+class SkEXRImageEncoder : public SkImageEncoder { |
+protected: |
+ bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) override; |
+private: |
+ typedef SkImageEncoder INHERITED; |
+}; |
+ |
+static inline bool encode_f16(SkWStream* stream, const SkBitmap& bitmap) { |
+ bool isOpaque = bitmap.isOpaque(); |
+ |
+ EXRHeader header; |
+ InitEXRHeader(&header); |
+ header.compression_type = TINYEXR_COMPRESSIONTYPE_PIZ; |
+ |
+ EXRChannelInfo channels[4]; |
+ sk_bzero(channels, 4 * sizeof(EXRChannelInfo)); |
+ header.channels = channels; |
+ |
+ // EXR viewers expect BGR(A). |
+ if (isOpaque) { |
+ header.num_channels = 3; |
+ header.channels[0].name[0] = 'B'; |
+ header.channels[1].name[0] = 'G'; |
+ header.channels[2].name[0] = 'R'; |
+ } else { |
+ header.num_channels = 4; |
+ header.channels[0].name[0] = 'B'; |
+ header.channels[1].name[0] = 'G'; |
+ header.channels[2].name[0] = 'R'; |
+ header.channels[3].name[0] = 'A'; |
+ } |
+ |
+ int pixelTypes[] = { |
+ TINYEXR_PIXELTYPE_HALF, |
+ TINYEXR_PIXELTYPE_HALF, |
+ TINYEXR_PIXELTYPE_HALF, |
+ TINYEXR_PIXELTYPE_HALF, |
+ }; |
+ |
+ header.pixel_types = pixelTypes; |
+ header.requested_pixel_types = pixelTypes; |
+ |
+ EXRImage image; |
+ InitEXRImage(&image); |
+ |
+ image.num_channels = header.num_channels; |
+ |
+ int width = bitmap.width(); |
+ int height = bitmap.height(); |
+ SkAutoTMalloc<SkHalf> storage(image.num_channels * width * height); |
+ SkHalf* planes[4]; |
+ for (int i = 0; i < image.num_channels; i++) { |
+ planes[i] = storage.get() + i * width * height; |
+ } |
+ |
+ for (int y = 0; y < height; y++) { |
+ for (int x = 0; x < width; x++) { |
+ uint64_t pixel = *((uint64_t*) bitmap.getAddr(x, y)); |
+ planes[0][y * width + x] = (pixel >> 32) & 0xFFFF; |
+ planes[1][y * width + x] = (pixel >> 16) & 0xFFFF; |
+ planes[2][y * width + x] = (pixel >> 0) & 0xFFFF; |
+ if (4 == image.num_channels) { |
+ planes[3][y * width + x] = (pixel >> 48) & 0xFFFF; |
+ } |
+ } |
+ } |
+ |
+ image.images = (uint8_t**) planes; |
+ image.width = width; |
+ image.height = height; |
+ |
+ const char* error; |
+ uint8_t* memory; |
+ size_t bytes = SaveEXRImageToMemory(&image, &header, &memory, &error); |
+ if (0 == bytes) { |
+ SkDebugf("EXR Error: %s\n", error); |
+ return false; |
+ } |
+ |
+ stream->write(memory, bytes); |
+ free(memory); |
+ return true; |
+} |
+ |
+bool SkEXRImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap, int) { |
+ const SkColorType colorType = bitmap.colorType(); |
+ const SkAlphaType alphaType = bitmap.alphaType(); |
+ switch (colorType) { |
+ case kAlpha_8_SkColorType: |
+ if (kPremul_SkAlphaType != alphaType && kUnpremul_SkAlphaType != alphaType) { |
+ return false; |
+ } |
+ break; |
+ case kRGBA_F16_SkColorType: |
+ if (kUnpremul_SkAlphaType == alphaType) { |
+ // OpenEXR expect premultiplied alpha. |
+ // FIXME (msarett): |
+ // Do the premultiply, then encode. |
+ return false; |
+ } |
+ |
+ return encode_f16(stream, bitmap); |
+ default: |
+ return false; |
+ } |
+ |
+ return true; |
+} |
+ |
+/////////////////////////////////////////////////////////////////////////////////////////////////// |
+ |
+DEFINE_ENCODER_CREATOR(EXRImageEncoder); |
+ |
+SkImageEncoder* sk_exr_efactory(SkImageEncoder::Type t) { |
+ return (SkImageEncoder::kEXR_Type == t) ? new SkEXRImageEncoder : nullptr; |
+} |
+ |
+static SkImageEncoder_EncodeReg gEReg(sk_exr_efactory); |