| 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);
|
| + }
|
| +}
|
|
|