Index: cc/output/color_lut_cache.cc |
diff --git a/cc/output/color_lut_cache.cc b/cc/output/color_lut_cache.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..f423c0f30db784641d3b9a4a31e4eaeb0a093309 |
--- /dev/null |
+++ b/cc/output/color_lut_cache.cc |
@@ -0,0 +1,149 @@ |
+// Copyright 2016 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "cc/output/color_lut_cache.h" |
+ |
+#include <stdint.h> |
+#include <vector> |
+ |
+#include "gpu/command_buffer/client/gles2_interface.h" |
+#include "third_party/qcms/src/qcms.h" |
+ |
+#ifndef THIS_MUST_BE_INCLUDED_AFTER_QCMS_H |
+extern "C" { |
+#include "third_party/qcms/src/chain.h" |
+}; |
+#endif |
+ |
+// After a LUT has not been used for this many frames, we release it. |
+const uint32_t kMaxFramesUnused = 10; |
+ |
+ColorLUTCache::ColorLUTCache(gpu::gles2::GLES2Interface* gl) |
+ : lut_cache_(0), gl_(gl) {} |
+ |
+ColorLUTCache::~ColorLUTCache() { |
+ GLuint textures[10]; |
+ size_t n = 0; |
+ for (const auto& cache_entry : lut_cache_) { |
+ textures[n++] = cache_entry.second.first; |
+ if (n == arraysize(textures)) { |
+ gl_->DeleteTextures(n, textures); |
+ n = 0; |
+ } |
+ } |
+ if (n) |
+ gl_->DeleteTextures(n, textures); |
+} |
+ |
+namespace { |
+ |
+// QCMS is trixy, it has a datatype called qcms_CIE_xyY, but what it expects |
+// is in fact not xyY color coordinates, it just wants the x/y values of the |
+// primaries with Y equal to 1.0. |
+qcms_CIE_xyY xyfromXY(const media::VideoColorSpace::XY& xy) { |
+ qcms_CIE_xyY ret; |
+ ret.x = xy.first; |
+ ret.y = xy.second; |
+ ret.Y = 1.0f; |
+ return ret; |
+} |
+ |
+} // namespace |
+ |
+unsigned int ColorLUTCache::MakeLUT(const media::VideoColorSpace& from, |
+ const gfx::ColorSpace& to, |
+ int lut_samples) { |
+ media::VideoColorSpace intermediate = media::VideoColorSpace::XYZ(); |
+ media::ColorTransform from_transform(from, intermediate, |
+ media::ColorTransform::INTENT_PERCEIVED); |
+ |
+ std::vector<media::VideoColorSpace::XY> primaries = |
+ intermediate.GetPrimaries(); |
+ qcms_CIE_xyYTRIPLE rgb; |
+ qcms_CIE_xyY w; |
+ rgb.red = xyfromXY(primaries[0]); |
+ rgb.green = xyfromXY(primaries[1]); |
+ rgb.blue = xyfromXY(primaries[2]); |
+ w = xyfromXY(primaries[3]); |
+ qcms_profile* in = qcms_profile_create_rgb_with_gamma(w, rgb, 1.0f); |
+ |
+ const std::vector<char>& icc_data = to.GetICCProfile(); |
+ qcms_profile* out; |
+ if (icc_data.empty()) { |
+ out = qcms_profile_sRGB(); |
+ } else { |
+ out = qcms_profile_from_memory(icc_data.data(), icc_data.size()); |
+ } |
+ |
+ int lut_entries = lut_samples * lut_samples * lut_samples; |
+ float inverse = 1.0f / (lut_samples - 1); |
+ std::vector<float> xyz_tmp(lut_entries * 3); |
+ int pos = 0; |
+ for (int a = 0; a < lut_samples; a++) { |
+ for (int b = 0; b < lut_samples; b++) { |
+ for (int c = 0; c < lut_samples; c++) { |
+ media::VideoColorSpace::TriStim tmp; |
+ tmp.values[0] = a * inverse; |
+ tmp.values[2] = b * inverse; |
+ tmp.values[1] = c * inverse; |
+ from_transform.transform(&tmp, 1); |
+ xyz_tmp[pos++] = fmax(0.0f, fmin(1.0f, tmp.values[0])); |
+ xyz_tmp[pos++] = fmax(0.0f, fmin(1.0f, tmp.values[1])); |
+ xyz_tmp[pos++] = fmax(0.0f, fmin(1.0f, tmp.values[2])); |
+ } |
+ } |
+ } |
+ qcms_chain_transform(in, out, xyz_tmp.data(), xyz_tmp.data(), |
+ lut_entries * 3); |
+ qcms_profile_release(in); |
+ qcms_profile_release(out); |
+ |
+ std::vector<unsigned char> lut(lut_samples * lut_samples * lut_samples * 4); |
+ unsigned char* lutp = lut.data(); |
+ pos = 0; |
+ for (int i = 0; i < lut_entries; i++) { |
+ for (int channel = 0; channel < 3; channel++) { |
+ int v = (int)floorf(xyz_tmp[pos++] * 255.0 + 0.5); |
+ *(lutp++) = v < 0 ? 0 : v > 255 ? 255 : v; |
+ } |
+ *(lutp++) = 255; |
+ } |
+ |
+ unsigned int lut_texture; |
+ gl_->GenTextures(1, &lut_texture); |
+ gl_->BindTexture(GL_TEXTURE_2D, lut_texture); |
+ gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
+ gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
+ gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
+ gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
+ gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, lut_samples, |
+ lut_samples * lut_samples, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
+ lut.data()); |
+ return lut_texture; |
+} |
+ |
+unsigned int ColorLUTCache::GetLUT(const media::VideoColorSpace& from, |
+ const gfx::ColorSpace& to, |
+ int lut_samples) { |
+ CacheKey key(from, std::make_pair(to, lut_samples)); |
+ auto iter = lut_cache_.Get(key); |
+ if (iter != lut_cache_.end()) { |
+ iter->second.second = current_frame_; |
+ return iter->second.first; |
+ } |
+ |
+ unsigned int lut = MakeLUT(from, to, lut_samples); |
+ lut_cache_.Put(key, std::make_pair(lut, current_frame_)); |
+ return lut; |
+} |
+ |
+void ColorLUTCache::Swap() { |
+ current_frame_++; |
+ while (!lut_cache_.empty() && |
+ current_frame_ - lut_cache_.rbegin()->second.first > |
+ kMaxFramesUnused) { |
+ gl_->DeleteTextures(1, &lut_cache_.rbegin()->second.first); |
+ lut_cache_.ShrinkToSize(lut_cache_.size() - 1); |
+ } |
+} |