OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "cc/output/color_lut_cache.h" |
| 6 |
| 7 #include <stdint.h> |
| 8 #include <vector> |
| 9 |
| 10 #include "gpu/command_buffer/client/gles2_interface.h" |
| 11 #include "third_party/qcms/src/qcms.h" |
| 12 |
| 13 #ifndef THIS_MUST_BE_INCLUDED_AFTER_QCMS_H |
| 14 extern "C" { |
| 15 #include "third_party/qcms/src/chain.h" |
| 16 }; |
| 17 #endif |
| 18 |
| 19 // After a LUT has not been used for this many frames, we release it. |
| 20 const uint32_t kMaxFramesUnused = 10; |
| 21 |
| 22 ColorLUTCache::ColorLUTCache(gpu::gles2::GLES2Interface* gl) |
| 23 : lut_cache_(0), gl_(gl) {} |
| 24 |
| 25 ColorLUTCache::~ColorLUTCache() { |
| 26 GLuint textures[10]; |
| 27 size_t n = 0; |
| 28 for (const auto& cache_entry : lut_cache_) { |
| 29 textures[n++] = cache_entry.second.first; |
| 30 if (n == arraysize(textures)) { |
| 31 gl_->DeleteTextures(n, textures); |
| 32 n = 0; |
| 33 } |
| 34 } |
| 35 if (n) |
| 36 gl_->DeleteTextures(n, textures); |
| 37 } |
| 38 |
| 39 namespace { |
| 40 |
| 41 // QCMS is trixy, it has a datatype called qcms_CIE_xyY, but what it expects |
| 42 // is in fact not xyY color coordinates, it just wants the x/y values of the |
| 43 // primaries with Y equal to 1.0. |
| 44 qcms_CIE_xyY xyfromXY(const media::VideoColorSpace::XY& xy) { |
| 45 qcms_CIE_xyY ret; |
| 46 ret.x = xy.first; |
| 47 ret.y = xy.second; |
| 48 ret.Y = 1.0f; |
| 49 return ret; |
| 50 } |
| 51 |
| 52 } // namespace |
| 53 |
| 54 unsigned int ColorLUTCache::MakeLUT(const media::VideoColorSpace& from, |
| 55 const gfx::ColorSpace& to, |
| 56 int lut_samples) { |
| 57 media::VideoColorSpace intermediate = media::VideoColorSpace::XYZ(); |
| 58 media::ColorTransform from_transform(from, intermediate, |
| 59 media::ColorTransform::INTENT_PERCEIVED); |
| 60 |
| 61 std::vector<media::VideoColorSpace::XY> primaries = |
| 62 intermediate.GetPrimaries(); |
| 63 qcms_CIE_xyYTRIPLE rgb; |
| 64 qcms_CIE_xyY w; |
| 65 rgb.red = xyfromXY(primaries[0]); |
| 66 rgb.green = xyfromXY(primaries[1]); |
| 67 rgb.blue = xyfromXY(primaries[2]); |
| 68 w = xyfromXY(primaries[3]); |
| 69 qcms_profile* in = qcms_profile_create_rgb_with_gamma(w, rgb, 1.0f); |
| 70 |
| 71 const std::vector<char>& icc_data = to.GetICCProfile(); |
| 72 qcms_profile* out; |
| 73 if (icc_data.empty()) { |
| 74 out = qcms_profile_sRGB(); |
| 75 } else { |
| 76 out = qcms_profile_from_memory(icc_data.data(), icc_data.size()); |
| 77 } |
| 78 |
| 79 int lut_entries = lut_samples * lut_samples * lut_samples; |
| 80 float inverse = 1.0f / (lut_samples - 1); |
| 81 std::vector<float> xyz_tmp(lut_entries * 3); |
| 82 int pos = 0; |
| 83 for (int a = 0; a < lut_samples; a++) { |
| 84 for (int b = 0; b < lut_samples; b++) { |
| 85 for (int c = 0; c < lut_samples; c++) { |
| 86 media::VideoColorSpace::TriStim tmp; |
| 87 tmp.values[0] = a * inverse; |
| 88 tmp.values[2] = b * inverse; |
| 89 tmp.values[1] = c * inverse; |
| 90 from_transform.transform(&tmp, 1); |
| 91 xyz_tmp[pos++] = fmax(0.0f, fmin(1.0f, tmp.values[0])); |
| 92 xyz_tmp[pos++] = fmax(0.0f, fmin(1.0f, tmp.values[1])); |
| 93 xyz_tmp[pos++] = fmax(0.0f, fmin(1.0f, tmp.values[2])); |
| 94 } |
| 95 } |
| 96 } |
| 97 qcms_chain_transform(in, out, xyz_tmp.data(), xyz_tmp.data(), |
| 98 lut_entries * 3); |
| 99 qcms_profile_release(in); |
| 100 qcms_profile_release(out); |
| 101 |
| 102 std::vector<unsigned char> lut(lut_samples * lut_samples * lut_samples * 4); |
| 103 unsigned char* lutp = lut.data(); |
| 104 pos = 0; |
| 105 for (int i = 0; i < lut_entries; i++) { |
| 106 for (int channel = 0; channel < 3; channel++) { |
| 107 int v = (int)floorf(xyz_tmp[pos++] * 255.0 + 0.5); |
| 108 *(lutp++) = v < 0 ? 0 : v > 255 ? 255 : v; |
| 109 } |
| 110 *(lutp++) = 255; |
| 111 } |
| 112 |
| 113 unsigned int lut_texture; |
| 114 gl_->GenTextures(1, &lut_texture); |
| 115 gl_->BindTexture(GL_TEXTURE_2D, lut_texture); |
| 116 gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| 117 gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| 118 gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| 119 gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| 120 gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, lut_samples, |
| 121 lut_samples * lut_samples, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| 122 lut.data()); |
| 123 return lut_texture; |
| 124 } |
| 125 |
| 126 unsigned int ColorLUTCache::GetLUT(const media::VideoColorSpace& from, |
| 127 const gfx::ColorSpace& to, |
| 128 int lut_samples) { |
| 129 CacheKey key(from, std::make_pair(to, lut_samples)); |
| 130 auto iter = lut_cache_.Get(key); |
| 131 if (iter != lut_cache_.end()) { |
| 132 iter->second.second = current_frame_; |
| 133 return iter->second.first; |
| 134 } |
| 135 |
| 136 unsigned int lut = MakeLUT(from, to, lut_samples); |
| 137 lut_cache_.Put(key, std::make_pair(lut, current_frame_)); |
| 138 return lut; |
| 139 } |
| 140 |
| 141 void ColorLUTCache::Swap() { |
| 142 current_frame_++; |
| 143 while (!lut_cache_.empty() && |
| 144 current_frame_ - lut_cache_.rbegin()->second.first > |
| 145 kMaxFramesUnused) { |
| 146 gl_->DeleteTextures(1, &lut_cache_.rbegin()->second.first); |
| 147 lut_cache_.ShrinkToSize(lut_cache_.size() - 1); |
| 148 } |
| 149 } |
OLD | NEW |