190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved. 290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// found in the LICENSE file. 490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "content/common/gpu/client/gl_helper_scaling.h" 690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include <deque> 890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include <string> 990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include <vector> 1090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 1190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "base/bind.h" 1290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "base/debug/trace_event.h" 1390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "base/lazy_instance.h" 1490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "base/logging.h" 1590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "base/memory/ref_counted.h" 169ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch#include "base/message_loop/message_loop.h" 17eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/time/time.h" 185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "gpu/command_buffer/client/gles2_interface.h" 1990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "third_party/skia/include/core/SkRegion.h" 2090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "ui/gfx/rect.h" 2190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "ui/gfx/size.h" 2290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)using gpu::gles2::GLES2Interface; 2490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 2590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)namespace content { 2690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)GLHelperScaling::GLHelperScaling(GLES2Interface* gl, GLHelper* helper) 285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) : gl_(gl), helper_(helper), vertex_attributes_buffer_(gl_) { 2990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) InitBuffer(); 3090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)} 3190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)GLHelperScaling::~GLHelperScaling() {} 3390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 3490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// Used to keep track of a generated shader program. The program 3590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// is passed in as text through Setup and is used by calling 365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// UseProgram() with the right parameters. Note that |gl_| 3790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// and |helper_| are assumed to live longer than this program. 3890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)class ShaderProgram : public base::RefCounted<ShaderProgram> { 3990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) public: 405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) ShaderProgram(GLES2Interface* gl, GLHelper* helper) 415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) : gl_(gl), 4290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) helper_(helper), 435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) program_(gl_->CreateProgram()), 445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) position_location_(-1), 455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) texcoord_location_(-1), 465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) src_subrect_location_(-1), 475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) src_pixelsize_location_(-1), 485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) dst_pixelsize_location_(-1), 495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scaling_vector_location_(-1), 505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) color_weights_location_(-1) {} 515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Compile shader program. 535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) void Setup(const GLchar* vertex_shader_text, 545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const GLchar* fragment_shader_text); 5590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 5690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // UseProgram must be called with GL_TEXTURE_2D bound to the 5790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // source texture and GL_ARRAY_BUFFER bound to a vertex 5890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // attribute buffer. 5990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) void UseProgram(const gfx::Size& src_size, 6090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) const gfx::Rect& src_subrect, 6190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) const gfx::Size& dst_size, 6290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) bool scale_x, 63868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) bool flip_y, 64868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) GLfloat color_weights[4]); 6590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) bool Initialized() const { return position_location_ != -1; } 675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 6890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) private: 6990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) friend class base::RefCounted<ShaderProgram>; 705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) ~ShaderProgram() { gl_->DeleteProgram(program_); } 7190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) GLES2Interface* gl_; 7390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) GLHelper* helper_; 7490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 7590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // A program for copying a source texture into a destination texture. 765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) GLuint program_; 7790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 7890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // The location of the position in the program. 795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) GLint position_location_; 8090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // The location of the texture coordinate in the program. 815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) GLint texcoord_location_; 8290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // The location of the source texture in the program. 835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) GLint texture_location_; 8490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // The location of the texture coordinate of 8590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // the sub-rectangle in the program. 865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) GLint src_subrect_location_; 8790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // Location of size of source image in pixels. 885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) GLint src_pixelsize_location_; 8990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // Location of size of destination image in pixels. 905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) GLint dst_pixelsize_location_; 9190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // Location of vector for scaling direction. 925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) GLint scaling_vector_location_; 93868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // Location of color weights. 945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) GLint color_weights_location_; 9590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 9690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) DISALLOW_COPY_AND_ASSIGN(ShaderProgram); 9790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}; 9890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 9990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// Implementation of a single stage in a scaler pipeline. If the pipeline has 10090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// multiple stages, it calls Scale() on the subscaler, then further scales the 10190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// output. Caches textures and framebuffers to avoid allocating/deleting 10290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// them once per frame, which can be expensive on some drivers. 1035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)class ScalerImpl : public GLHelper::ScalerInterface, 1045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) public GLHelperScaling::ShaderInterface { 10590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) public: 1065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // |gl| and |copy_impl| are expected to live longer than this object. 10790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // |src_size| is the size of the input texture in pixels. 10890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // |dst_size| is the size of the output texutre in pixels. 10990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // |src_subrect| is the portion of the src to copy to the output texture. 11090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // If |scale_x| is true, we are scaling along the X axis, otherwise Y. 11190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // If we are scaling in both X and Y, |scale_x| is ignored. 11290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // If |vertically_flip_texture| is true, output will be upside-down. 11390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // If |swizzle| is true, RGBA will be transformed into BGRA. 114868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // |color_weights| are only used together with SHADER_PLANAR to specify 115868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // how to convert RGB colors into a single value. 1165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) ScalerImpl(GLES2Interface* gl, 11790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) GLHelperScaling* scaler_helper, 1185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const GLHelperScaling::ScalerStage& scaler_stage, 119868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) ScalerImpl* subscaler, 1205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const float* color_weights) 1215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) : gl_(gl), 1225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scaler_helper_(scaler_helper), 1235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) spec_(scaler_stage), 1245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) intermediate_texture_(0), 1255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) dst_framebuffer_(gl), 1265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) subscaler_(subscaler) { 127868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) if (color_weights) { 128868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) color_weights_[0] = color_weights[0]; 129868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) color_weights_[1] = color_weights[1]; 130868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) color_weights_[2] = color_weights[2]; 131868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) color_weights_[3] = color_weights[3]; 132868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) } else { 133868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) color_weights_[0] = 0.0; 134868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) color_weights_[1] = 0.0; 135868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) color_weights_[2] = 0.0; 136868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) color_weights_[3] = 0.0; 137868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) } 1385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) shader_program_ = 1395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scaler_helper_->GetShaderProgram(spec_.shader, spec_.swizzle); 14090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 14190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (subscaler_) { 1425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) intermediate_texture_ = 0u; 1435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gl_->GenTextures(1, &intermediate_texture_); 1445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, 1455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) intermediate_texture_); 1465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gl_->TexImage2D(GL_TEXTURE_2D, 1475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 0, 1485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) GL_RGBA, 1495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) spec_.src_size.width(), 1505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) spec_.src_size.height(), 1515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 0, 1525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) GL_RGBA, 1535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) GL_UNSIGNED_BYTE, 1545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) NULL); 15590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 15690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 15790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 15890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) virtual ~ScalerImpl() { 15990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (intermediate_texture_) { 1605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gl_->DeleteTextures(1, &intermediate_texture_); 16190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 16290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 16390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 164868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // GLHelperShader::ShaderInterface implementation. 1655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) virtual void Execute(GLuint source_texture, 1665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const std::vector<GLuint>& dest_textures) OVERRIDE { 16790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (subscaler_) { 16890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) subscaler_->Scale(source_texture, intermediate_texture_); 16990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) source_texture = intermediate_texture_; 17090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 17190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 17290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder( 1735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gl_, dst_framebuffer_); 174868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) DCHECK_GT(dest_textures.size(), 0U); 1755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scoped_ptr<GLenum[]> buffers(new GLenum[dest_textures.size()]); 176868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) for (size_t t = 0; t < dest_textures.size(); t++) { 1775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, dest_textures[t]); 1785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gl_->FramebufferTexture2D(GL_FRAMEBUFFER, 1795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) GL_COLOR_ATTACHMENT0 + t, 1805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) GL_TEXTURE_2D, 1815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) dest_textures[t], 1825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 0); 183868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) buffers[t] = GL_COLOR_ATTACHMENT0 + t; 18490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 1855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, source_texture); 1865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 1885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 1895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 1905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 19190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 19290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) ScopedBufferBinder<GL_ARRAY_BUFFER> buffer_binder( 1935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gl_, scaler_helper_->vertex_attributes_buffer_); 1945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DCHECK(shader_program_->Initialized()); 19590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) shader_program_->UseProgram(spec_.src_size, 19690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) spec_.src_subrect, 19790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) spec_.dst_size, 19890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) spec_.scale_x, 199868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) spec_.vertically_flip_texture, 200868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) color_weights_); 2015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gl_->Viewport(0, 0, spec_.dst_size.width(), spec_.dst_size.height()); 20290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 203868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) if (dest_textures.size() > 1) { 204868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) DCHECK_LE(static_cast<int>(dest_textures.size()), 205868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) scaler_helper_->helper_->MaxDrawBuffers()); 2065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gl_->DrawBuffersEXT(dest_textures.size(), buffers.get()); 207868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) } 20890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // Conduct texture mapping by drawing a quad composed of two triangles. 2095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gl_->DrawArrays(GL_TRIANGLE_STRIP, 0, 4); 210868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) if (dest_textures.size() > 1) { 211868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // Set the draw buffers back to not confuse others. 2125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gl_->DrawBuffersEXT(1, &buffers[0]); 213868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) } 214868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) } 215868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 216868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // GLHelper::ScalerInterface implementation. 2175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) virtual void Scale(GLuint source_texture, GLuint dest_texture) OVERRIDE { 2185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) std::vector<GLuint> tmp(1); 219868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) tmp[0] = dest_texture; 220868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) Execute(source_texture, tmp); 22190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 22290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 22390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) virtual const gfx::Size& SrcSize() OVERRIDE { 22490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (subscaler_) { 22590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) return subscaler_->SrcSize(); 22690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 22790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) return spec_.src_size; 22890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 22990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) virtual const gfx::Rect& SrcSubrect() OVERRIDE { 23090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (subscaler_) { 23190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) return subscaler_->SrcSubrect(); 23290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 23390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) return spec_.src_subrect; 23490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 2355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) virtual const gfx::Size& DstSize() OVERRIDE { return spec_.dst_size; } 23690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 23790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) private: 2385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) GLES2Interface* gl_; 23990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) GLHelperScaling* scaler_helper_; 24090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) GLHelperScaling::ScalerStage spec_; 241868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) GLfloat color_weights_[4]; 2425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) GLuint intermediate_texture_; 24390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) scoped_refptr<ShaderProgram> shader_program_; 24490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) ScopedFramebuffer dst_framebuffer_; 24590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) scoped_ptr<ScalerImpl> subscaler_; 24690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}; 24790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 2485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)GLHelperScaling::ScalerStage::ScalerStage(ShaderType shader_, 2495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gfx::Size src_size_, 2505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gfx::Rect src_subrect_, 2515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gfx::Size dst_size_, 2525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) bool scale_x_, 2535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) bool vertically_flip_texture_, 2545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) bool swizzle_) 25590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) : shader(shader_), 25690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) src_size(src_size_), 25790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) src_subrect(src_subrect_), 25890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) dst_size(dst_size_), 25990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) scale_x(scale_x_), 26090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) vertically_flip_texture(vertically_flip_texture_), 2615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) swizzle(swizzle_) {} 26290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 26390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// The important inputs for this function is |x_ops| and 26490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// |y_ops|. They represent scaling operations to be done 26590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// on an imag of size |src_size|. If |quality| is SCALER_QUALITY_BEST, 26690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// then we will interpret these scale operations literally and we'll 26790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// create one scaler stage for each ScaleOp. However, if |quality| 26890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// is SCALER_QUALITY_GOOD, then we can do a whole bunch of optimizations 26990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// by combining two or more ScaleOps in to a single scaler stage. 27090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// Normally we process ScaleOps from |y_ops| first and |x_ops| after 27190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// all |y_ops| are processed, but sometimes we can combine one or more 27290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// operation from both queues essentially for free. This is the reason 27390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// why |x_ops| and |y_ops| aren't just one single queue. 27490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)void GLHelperScaling::ConvertScalerOpsToScalerStages( 27590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) GLHelper::ScalerQuality quality, 27690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) gfx::Size src_size, 27790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) gfx::Rect src_subrect, 27890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) const gfx::Size& dst_size, 27990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) bool vertically_flip_texture, 28090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) bool swizzle, 28190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) std::deque<GLHelperScaling::ScaleOp>* x_ops, 28290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) std::deque<GLHelperScaling::ScaleOp>* y_ops, 2835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) std::vector<ScalerStage>* scaler_stages) { 28490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) while (!x_ops->empty() || !y_ops->empty()) { 28590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) gfx::Size intermediate_size = src_subrect.size(); 28690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) std::deque<ScaleOp>* current_queue = NULL; 28790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 28890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (!y_ops->empty()) { 28990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) current_queue = y_ops; 29090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } else { 29190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) current_queue = x_ops; 29290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 29390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 29490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) ShaderType current_shader = SHADER_BILINEAR; 29590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) switch (current_queue->front().scale_factor) { 29690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) case 0: 29790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (quality == GLHelper::SCALER_QUALITY_BEST) { 29890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) current_shader = SHADER_BICUBIC_UPSCALE; 29990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 30090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) break; 30190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) case 2: 30290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (quality == GLHelper::SCALER_QUALITY_BEST) { 30390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) current_shader = SHADER_BICUBIC_HALF_1D; 30490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 30590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) break; 30690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) case 3: 30790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) DCHECK(quality != GLHelper::SCALER_QUALITY_BEST); 30890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) current_shader = SHADER_BILINEAR3; 30990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) break; 31090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) default: 31190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) NOTREACHED(); 31290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 31390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) bool scale_x = current_queue->front().scale_x; 31490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) current_queue->front().UpdateSize(&intermediate_size); 31590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) current_queue->pop_front(); 31690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 31790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // Optimization: Sometimes we can combine 2-4 scaling operations into 31890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // one operation. 31990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (quality == GLHelper::SCALER_QUALITY_GOOD) { 32090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (!current_queue->empty() && current_shader == SHADER_BILINEAR) { 32190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // Combine two steps in the same dimension. 32290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) current_queue->front().UpdateSize(&intermediate_size); 32390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) current_queue->pop_front(); 32490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) current_shader = SHADER_BILINEAR2; 32590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (!current_queue->empty()) { 32690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // Combine three steps in the same dimension. 32790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) current_queue->front().UpdateSize(&intermediate_size); 32890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) current_queue->pop_front(); 32990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) current_shader = SHADER_BILINEAR4; 33090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 33190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 33290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // Check if we can combine some steps in the other dimension as well. 33390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // Since all shaders currently use GL_LINEAR, we can easily scale up 33490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // or scale down by exactly 2x at the same time as we do another 33590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // operation. Currently, the following mergers are supported: 33690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // * 1 bilinear Y-pass with 1 bilinear X-pass (up or down) 33790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // * 2 bilinear Y-passes with 2 bilinear X-passes 33890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // * 1 bilinear Y-pass with N bilinear X-pass 33990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // * N bilinear Y-passes with 1 bilinear X-pass (down only) 34090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // Measurements indicate that generalizing this for 3x3 and 4x4 34190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // makes it slower on some platforms, such as the Pixel. 3425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (!scale_x && x_ops->size() > 0 && x_ops->front().scale_factor <= 2) { 34390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) int x_passes = 0; 34490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (current_shader == SHADER_BILINEAR2 && x_ops->size() >= 2) { 34590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // 2y + 2x passes 34690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) x_passes = 2; 34790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) current_shader = SHADER_BILINEAR2X2; 34890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } else if (current_shader == SHADER_BILINEAR) { 34990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // 1y + Nx passes 35090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) scale_x = true; 35190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) switch (x_ops->size()) { 35290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) case 0: 35390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) NOTREACHED(); 35490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) case 1: 35590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (x_ops->front().scale_factor == 3) { 35690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) current_shader = SHADER_BILINEAR3; 35790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 35890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) x_passes = 1; 35990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) break; 36090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) case 2: 36190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) x_passes = 2; 36290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) current_shader = SHADER_BILINEAR2; 36390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) break; 36490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) default: 36590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) x_passes = 3; 36690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) current_shader = SHADER_BILINEAR4; 36790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) break; 36890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 36990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } else if (x_ops->front().scale_factor == 2) { 37090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // Ny + 1x-downscale 37190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) x_passes = 1; 37290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 37390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 37490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) for (int i = 0; i < x_passes; i++) { 37590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) x_ops->front().UpdateSize(&intermediate_size); 37690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) x_ops->pop_front(); 37790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 37890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 37990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 38090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 38190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) scaler_stages->push_back(ScalerStage(current_shader, 38290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) src_size, 38390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) src_subrect, 38490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) intermediate_size, 38590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) scale_x, 38690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) vertically_flip_texture, 38790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) swizzle)); 38890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) src_size = intermediate_size; 38990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) src_subrect = gfx::Rect(intermediate_size); 39090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) vertically_flip_texture = false; 39190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) swizzle = false; 39290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 39390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)} 39490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 39590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)void GLHelperScaling::ComputeScalerStages( 39690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) GLHelper::ScalerQuality quality, 39790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) const gfx::Size& src_size, 39890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) const gfx::Rect& src_subrect, 39990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) const gfx::Size& dst_size, 40090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) bool vertically_flip_texture, 40190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) bool swizzle, 4025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) std::vector<ScalerStage>* scaler_stages) { 40390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (quality == GLHelper::SCALER_QUALITY_FAST || 40490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) src_subrect.size() == dst_size) { 40590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) scaler_stages->push_back(ScalerStage(SHADER_BILINEAR, 40690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) src_size, 40790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) src_subrect, 40890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) dst_size, 40990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) false, 41090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) vertically_flip_texture, 41190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) swizzle)); 41290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) return; 41390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 41490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 41590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) std::deque<GLHelperScaling::ScaleOp> x_ops, y_ops; 41690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) GLHelperScaling::ScaleOp::AddOps(src_subrect.width(), 41790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) dst_size.width(), 41890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) true, 41990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) quality == GLHelper::SCALER_QUALITY_GOOD, 42090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) &x_ops); 42190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) GLHelperScaling::ScaleOp::AddOps(src_subrect.height(), 42290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) dst_size.height(), 42390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) false, 42490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) quality == GLHelper::SCALER_QUALITY_GOOD, 42590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) &y_ops); 42690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 4275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) ConvertScalerOpsToScalerStages(quality, 4285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) src_size, 4295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) src_subrect, 4305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) dst_size, 4315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) vertically_flip_texture, 4325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) swizzle, 4335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) &x_ops, 4345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) &y_ops, 4355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scaler_stages); 43690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)} 43790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 4385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)GLHelper::ScalerInterface* GLHelperScaling::CreateScaler( 4395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) GLHelper::ScalerQuality quality, 4405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gfx::Size src_size, 4415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gfx::Rect src_subrect, 4425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const gfx::Size& dst_size, 4435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) bool vertically_flip_texture, 4445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) bool swizzle) { 44590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) std::vector<ScalerStage> scaler_stages; 44690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) ComputeScalerStages(quality, 44790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) src_size, 44890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) src_subrect, 44990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) dst_size, 45090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) vertically_flip_texture, 45190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) swizzle, 45290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) &scaler_stages); 45390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 45490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) ScalerImpl* ret = NULL; 45590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) for (unsigned int i = 0; i < scaler_stages.size(); i++) { 4565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) ret = new ScalerImpl(gl_, this, scaler_stages[i], ret, NULL); 45790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 45890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) return ret; 45990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)} 46090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 4615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)GLHelper::ScalerInterface* GLHelperScaling::CreatePlanarScaler( 462868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) const gfx::Size& src_size, 463868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) const gfx::Rect& src_subrect, 464868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) const gfx::Size& dst_size, 465868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) bool vertically_flip_texture, 466effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch bool swizzle, 467868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) const float color_weights[4]) { 468868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) ScalerStage stage(SHADER_PLANAR, 469868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) src_size, 470868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) src_subrect, 471868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) dst_size, 472868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) true, 473868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) vertically_flip_texture, 474effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch swizzle); 4755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return new ScalerImpl(gl_, this, stage, NULL, color_weights); 476868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)} 477868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 4785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)GLHelperScaling::ShaderInterface* GLHelperScaling::CreateYuvMrtShader( 479868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) const gfx::Size& src_size, 480868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) const gfx::Rect& src_subrect, 481868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) const gfx::Size& dst_size, 482868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) bool vertically_flip_texture, 483effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch bool swizzle, 484868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) ShaderType shader) { 485868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) DCHECK(shader == SHADER_YUV_MRT_PASS1 || shader == SHADER_YUV_MRT_PASS2); 486868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) ScalerStage stage(shader, 487868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) src_size, 488868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) src_subrect, 489868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) dst_size, 490868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) true, 491868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) vertically_flip_texture, 492effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch swizzle); 4935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return new ScalerImpl(gl_, this, stage, NULL, NULL); 494868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)} 495868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 4965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const GLfloat GLHelperScaling::kVertexAttributes[] = { 4975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) -1.0f, -1.0f, 0.0f, 0.0f, // vertex 0 4985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1.0f, -1.0f, 1.0f, 0.0f, // vertex 1 4995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) -1.0f, 1.0f, 0.0f, 1.0f, // vertex 2 5005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1.0f, 1.0f, 1.0f, 1.0f, }; // vertex 3 50190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 50290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)void GLHelperScaling::InitBuffer() { 5035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) ScopedBufferBinder<GL_ARRAY_BUFFER> buffer_binder(gl_, 5045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) vertex_attributes_buffer_); 5055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gl_->BufferData(GL_ARRAY_BUFFER, 5065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) sizeof(kVertexAttributes), 5075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) kVertexAttributes, 5085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) GL_STATIC_DRAW); 50990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)} 51090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 5115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)scoped_refptr<ShaderProgram> GLHelperScaling::GetShaderProgram(ShaderType type, 5125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) bool swizzle) { 51390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) ShaderProgramKeyType key(type, swizzle); 51490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) scoped_refptr<ShaderProgram>& cache_entry(shader_programs_[key]); 515868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) if (!cache_entry.get()) { 5165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) cache_entry = new ShaderProgram(gl_, helper_); 5175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) std::basic_string<GLchar> vertex_program; 5185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) std::basic_string<GLchar> fragment_program; 5195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) std::basic_string<GLchar> vertex_header; 5205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) std::basic_string<GLchar> fragment_directives; 5215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) std::basic_string<GLchar> fragment_header; 5225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) std::basic_string<GLchar> shared_variables; 52390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 52490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) vertex_header.append( 52590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) "precision highp float;\n" 52690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) "attribute vec2 a_position;\n" 5273551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) "attribute vec2 a_texcoord;\n" 5283551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) "uniform vec4 src_subrect;\n"); 52990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 53090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) fragment_header.append( 53190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) "precision mediump float;\n" 53290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) "uniform sampler2D s_texture;\n"); 53390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 53490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) vertex_program.append( 53590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " gl_Position = vec4(a_position, 0.0, 1.0);\n" 5367d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) " vec2 texcoord = src_subrect.xy + a_texcoord * src_subrect.zw;\n"); 53790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 53890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) switch (type) { 53990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) case SHADER_BILINEAR: 54090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) shared_variables.append("varying vec2 v_texcoord;\n"); 54190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) vertex_program.append(" v_texcoord = texcoord;\n"); 54290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) fragment_program.append( 54390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " gl_FragColor = texture2D(s_texture, v_texcoord);\n"); 54490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) break; 54590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 54690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) case SHADER_BILINEAR2: 54790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // This is equivialent to two passes of the BILINEAR shader above. 54890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // It can be used to scale an image down 1.0x-2.0x in either dimension, 54990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // or exactly 4x. 55090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) shared_variables.append( 55190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) "varying vec4 v_texcoords;\n"); // 2 texcoords packed in one quad 5523551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) vertex_header.append( 5533551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) "uniform vec2 scaling_vector;\n" 5543551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) "uniform vec2 dst_pixelsize;\n"); 55590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) vertex_program.append( 5567d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n" 557868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " step /= 4.0;\n" 55890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " v_texcoords.xy = texcoord + step;\n" 55990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " v_texcoords.zw = texcoord - step;\n"); 56090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 56190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) fragment_program.append( 56290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " gl_FragColor = (texture2D(s_texture, v_texcoords.xy) +\n" 56390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " texture2D(s_texture, v_texcoords.zw)) / 2.0;\n"); 5643551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) break; 56590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 56690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) case SHADER_BILINEAR3: 56790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // This is kind of like doing 1.5 passes of the BILINEAR shader. 56890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // It can be used to scale an image down 1.5x-3.0x, or exactly 6x. 56990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) shared_variables.append( 57090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) "varying vec4 v_texcoords1;\n" // 2 texcoords packed in one quad 57190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) "varying vec2 v_texcoords2;\n"); 5723551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) vertex_header.append( 5733551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) "uniform vec2 scaling_vector;\n" 5743551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) "uniform vec2 dst_pixelsize;\n"); 57590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) vertex_program.append( 5767d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n" 577868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " step /= 3.0;\n" 57890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " v_texcoords1.xy = texcoord + step;\n" 57990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " v_texcoords1.zw = texcoord;\n" 58090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " v_texcoords2 = texcoord - step;\n"); 58190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) fragment_program.append( 58290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " gl_FragColor = (texture2D(s_texture, v_texcoords1.xy) +\n" 58390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " texture2D(s_texture, v_texcoords1.zw) +\n" 58490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " texture2D(s_texture, v_texcoords2)) / 3.0;\n"); 5853551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) break; 58690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 58790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) case SHADER_BILINEAR4: 58890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // This is equivialent to three passes of the BILINEAR shader above, 58990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // It can be used to scale an image down 2.0x-4.0x or exactly 8x. 5905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) shared_variables.append("varying vec4 v_texcoords[2];\n"); 5913551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) vertex_header.append( 5923551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) "uniform vec2 scaling_vector;\n" 5933551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) "uniform vec2 dst_pixelsize;\n"); 59490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) vertex_program.append( 5957d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n" 596868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " step /= 8.0;\n" 59790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " v_texcoords[0].xy = texcoord - step * 3.0;\n" 59890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " v_texcoords[0].zw = texcoord - step;\n" 59990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " v_texcoords[1].xy = texcoord + step;\n" 60090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " v_texcoords[1].zw = texcoord + step * 3.0;\n"); 60190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) fragment_program.append( 60290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " gl_FragColor = (\n" 60390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " texture2D(s_texture, v_texcoords[0].xy) +\n" 60490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " texture2D(s_texture, v_texcoords[0].zw) +\n" 60590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " texture2D(s_texture, v_texcoords[1].xy) +\n" 60690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " texture2D(s_texture, v_texcoords[1].zw)) / 4.0;\n"); 6073551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) break; 60890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 60990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) case SHADER_BILINEAR2X2: 61090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // This is equivialent to four passes of the BILINEAR shader above. 61190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // Two in each dimension. It can be used to scale an image down 61290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // 1.0x-2.0x in both X and Y directions. Or, it could be used to 61390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // scale an image down by exactly 4x in both dimensions. 6145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) shared_variables.append("varying vec4 v_texcoords[2];\n"); 6155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) vertex_header.append("uniform vec2 dst_pixelsize;\n"); 61690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) vertex_program.append( 6177d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) " vec2 step = src_subrect.zw / 4.0 / dst_pixelsize;\n" 61890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " v_texcoords[0].xy = texcoord + vec2(step.x, step.y);\n" 61990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " v_texcoords[0].zw = texcoord + vec2(step.x, -step.y);\n" 62090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " v_texcoords[1].xy = texcoord + vec2(-step.x, step.y);\n" 62190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " v_texcoords[1].zw = texcoord + vec2(-step.x, -step.y);\n"); 62290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) fragment_program.append( 62390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " gl_FragColor = (\n" 62490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " texture2D(s_texture, v_texcoords[0].xy) +\n" 62590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " texture2D(s_texture, v_texcoords[0].zw) +\n" 62690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " texture2D(s_texture, v_texcoords[1].xy) +\n" 62790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " texture2D(s_texture, v_texcoords[1].zw)) / 4.0;\n"); 6283551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) break; 62990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 63090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) case SHADER_BICUBIC_HALF_1D: 63190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // This scales down texture by exactly half in one dimension. 63290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // directions in one pass. We use bilinear lookup to reduce 63390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // the number of texture reads from 8 to 4 63490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) shared_variables.append( 63590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) "const float CenterDist = 99.0 / 140.0;\n" 63690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) "const float LobeDist = 11.0 / 4.0;\n" 63790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) "const float CenterWeight = 35.0 / 64.0;\n" 63890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) "const float LobeWeight = -3.0 / 64.0;\n" 63990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) "varying vec4 v_texcoords[2];\n"); 6403551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) vertex_header.append( 6413551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) "uniform vec2 scaling_vector;\n" 6423551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) "uniform vec2 src_pixelsize;\n"); 64390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) vertex_program.append( 6447d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) " vec2 step = src_subrect.zw * scaling_vector / src_pixelsize;\n" 64590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " v_texcoords[0].xy = texcoord - LobeDist * step;\n" 64690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " v_texcoords[0].zw = texcoord - CenterDist * step;\n" 64790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " v_texcoords[1].xy = texcoord + CenterDist * step;\n" 64890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " v_texcoords[1].zw = texcoord + LobeDist * step;\n"); 64990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) fragment_program.append( 65090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " gl_FragColor = \n" 65190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // Lobe pixels 65290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " (texture2D(s_texture, v_texcoords[0].xy) +\n" 65390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " texture2D(s_texture, v_texcoords[1].zw)) *\n" 65490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " LobeWeight +\n" 65590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // Center pixels 65690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " (texture2D(s_texture, v_texcoords[0].zw) +\n" 65790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " texture2D(s_texture, v_texcoords[1].xy)) *\n" 65890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " CenterWeight;\n"); 6595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) break; 66090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 66190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) case SHADER_BICUBIC_UPSCALE: 66290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // When scaling up, we need 4 texture reads, but we can 66390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // save some instructions because will know in which range of 66490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // the bicubic function each call call to the bicubic function 66590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // will be in. 66690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // Also, when sampling the bicubic function like this, the sum 66790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // is always exactly one, so we can skip normalization as well. 6685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) shared_variables.append("varying vec2 v_texcoord;\n"); 6695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) vertex_program.append(" v_texcoord = texcoord;\n"); 67090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) fragment_header.append( 6713551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) "uniform vec2 src_pixelsize;\n" 6723551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) "uniform vec2 scaling_vector;\n" 67390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) "const float a = -0.5;\n" 67490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // This function is equivialent to calling the bicubic 67590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // function with x-1, x, 1-x and 2-x 67690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // (assuming 0 <= x < 1) 67790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) "vec4 filt4(float x) {\n" 67890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " return vec4(x * x * x, x * x, x, 1) *\n" 67990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " mat4( a, -2.0 * a, a, 0.0,\n" 68090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " a + 2.0, -a - 3.0, 0.0, 1.0,\n" 68190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " -a - 2.0, 3.0 + 2.0 * a, -a, 0.0,\n" 68290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " -a, a, 0.0, 0.0);\n" 68390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) "}\n" 68490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) "mat4 pixels_x(vec2 pos, vec2 step) {\n" 68590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " return mat4(\n" 68690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " texture2D(s_texture, pos - step),\n" 68790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " texture2D(s_texture, pos),\n" 68890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " texture2D(s_texture, pos + step),\n" 68990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " texture2D(s_texture, pos + step * 2.0));\n" 69090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) "}\n"); 69190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) fragment_program.append( 69290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " vec2 pixel_pos = v_texcoord * src_pixelsize - \n" 69390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " scaling_vector / 2.0;\n" 69490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " float frac = fract(dot(pixel_pos, scaling_vector));\n" 69590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " vec2 base = (floor(pixel_pos) + vec2(0.5)) / src_pixelsize;\n" 69690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " vec2 step = scaling_vector / src_pixelsize;\n" 69790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) " gl_FragColor = pixels_x(base, step) * filt4(frac);\n"); 69890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) break; 699868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 700868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) case SHADER_PLANAR: 701868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // Converts four RGBA pixels into one pixel. Each RGBA 702868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // pixel will be dot-multiplied with the color weights and 703868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // then placed into a component of the output. This is used to 704868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // convert RGBA textures into Y, U and V textures. We do this 705868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // because single-component textures are not renderable on all 706868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // architectures. 7075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) shared_variables.append("varying vec4 v_texcoords[2];\n"); 7083551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) vertex_header.append( 7093551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) "uniform vec2 scaling_vector;\n" 7103551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) "uniform vec2 dst_pixelsize;\n"); 711868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) vertex_program.append( 7127d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n" 713868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " step /= 4.0;\n" 714868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " v_texcoords[0].xy = texcoord - step * 1.5;\n" 715868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " v_texcoords[0].zw = texcoord - step * 0.5;\n" 716868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " v_texcoords[1].xy = texcoord + step * 0.5;\n" 717868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " v_texcoords[1].zw = texcoord + step * 1.5;\n"); 7185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) fragment_header.append("uniform vec4 color_weights;\n"); 719868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) fragment_program.append( 720868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " gl_FragColor = color_weights * mat4(\n" 721868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " vec4(texture2D(s_texture, v_texcoords[0].xy).rgb, 1.0),\n" 722868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " vec4(texture2D(s_texture, v_texcoords[0].zw).rgb, 1.0),\n" 723868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " vec4(texture2D(s_texture, v_texcoords[1].xy).rgb, 1.0),\n" 724868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " vec4(texture2D(s_texture, v_texcoords[1].zw).rgb, 1.0));\n"); 725868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) break; 726868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 727868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) case SHADER_YUV_MRT_PASS1: 728868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // RGB24 to YV12 in two passes; writing two 8888 targets each pass. 729868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // 730868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // YV12 is full-resolution luma and half-resolution blue/red chroma. 731868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // 732868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // (original) 733868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX 734868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX 735868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX 736868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX 737868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX 738868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX 739868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // | 740868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // | (y plane) (temporary) 741868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // | YYYY YYYY UUVV UUVV 742868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // +--> { YYYY YYYY + UUVV UUVV } 743868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // YYYY YYYY UUVV UUVV 744868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // First YYYY YYYY UUVV UUVV 745868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // pass YYYY YYYY UUVV UUVV 746868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // YYYY YYYY UUVV UUVV 747868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // | 748868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // | (u plane) (v plane) 749868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // Second | UUUU VVVV 750868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // pass +--> { UUUU + VVVV } 751868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // UUUU VVVV 752868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // 7535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) shared_variables.append("varying vec4 v_texcoords[2];\n"); 7543551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) vertex_header.append( 7553551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) "uniform vec2 scaling_vector;\n" 7563551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) "uniform vec2 dst_pixelsize;\n"); 757868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) vertex_program.append( 7587d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n" 759868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " step /= 4.0;\n" 760868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " v_texcoords[0].xy = texcoord - step * 1.5;\n" 761868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " v_texcoords[0].zw = texcoord - step * 0.5;\n" 762868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " v_texcoords[1].xy = texcoord + step * 0.5;\n" 763868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " v_texcoords[1].zw = texcoord + step * 1.5;\n"); 7645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) fragment_directives.append("#extension GL_EXT_draw_buffers : enable\n"); 765868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) fragment_header.append( 766868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) "const vec3 kRGBtoY = vec3(0.257, 0.504, 0.098);\n" 767868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) "const float kYBias = 0.0625;\n" 768868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // Divide U and V by two to compensate for averaging below. 769868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) "const vec3 kRGBtoU = vec3(-0.148, -0.291, 0.439) / 2.0;\n" 770868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) "const vec3 kRGBtoV = vec3(0.439, -0.368, -0.071) / 2.0;\n" 771868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) "const float kUVBias = 0.5;\n"); 772868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) fragment_program.append( 773868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " vec3 pixel1 = texture2D(s_texture, v_texcoords[0].xy).rgb;\n" 774868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " vec3 pixel2 = texture2D(s_texture, v_texcoords[0].zw).rgb;\n" 775868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " vec3 pixel3 = texture2D(s_texture, v_texcoords[1].xy).rgb;\n" 776868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " vec3 pixel4 = texture2D(s_texture, v_texcoords[1].zw).rgb;\n" 777868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " vec3 pixel12 = pixel1 + pixel2;\n" 778868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " vec3 pixel34 = pixel3 + pixel4;\n" 779868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " gl_FragData[0] = vec4(dot(pixel1, kRGBtoY),\n" 780868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " dot(pixel2, kRGBtoY),\n" 781868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " dot(pixel3, kRGBtoY),\n" 782868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " dot(pixel4, kRGBtoY)) + kYBias;\n" 783868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " gl_FragData[1] = vec4(dot(pixel12, kRGBtoU),\n" 784868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " dot(pixel34, kRGBtoU),\n" 785868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " dot(pixel12, kRGBtoV),\n" 786868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " dot(pixel34, kRGBtoV)) + kUVBias;\n"); 787868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) break; 788868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 789868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) case SHADER_YUV_MRT_PASS2: 790868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // We're just sampling two pixels and unswizzling them. There's 791868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // no need to do vertical scaling with math, since bilinear 792868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // interpolation in the sampler takes care of that. 7935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) shared_variables.append("varying vec4 v_texcoords;\n"); 7943551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) vertex_header.append( 7953551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) "uniform vec2 scaling_vector;\n" 7963551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) "uniform vec2 dst_pixelsize;\n"); 797868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) vertex_program.append( 7987d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n" 799868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " step /= 2.0;\n" 800868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " v_texcoords.xy = texcoord - step * 0.5;\n" 801868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " v_texcoords.zw = texcoord + step * 0.5;\n"); 8025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) fragment_directives.append("#extension GL_EXT_draw_buffers : enable\n"); 803868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) fragment_program.append( 804868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " vec4 lo_uuvv = texture2D(s_texture, v_texcoords.xy);\n" 805868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " vec4 hi_uuvv = texture2D(s_texture, v_texcoords.zw);\n" 806868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " gl_FragData[0] = vec4(lo_uuvv.rg, hi_uuvv.rg);\n" 807868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) " gl_FragData[1] = vec4(lo_uuvv.ba, hi_uuvv.ba);\n"); 808868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) break; 80990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 81090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (swizzle) { 811effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch switch(type) { 812effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch case SHADER_YUV_MRT_PASS1: 813effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch fragment_program.append(" gl_FragData[0] = gl_FragData[0].bgra;\n"); 814effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch break; 815effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch case SHADER_YUV_MRT_PASS2: 816effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch fragment_program.append(" gl_FragData[0] = gl_FragData[0].bgra;\n"); 817effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch fragment_program.append(" gl_FragData[1] = gl_FragData[1].bgra;\n"); 818effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch break; 819effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch default: 820effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch fragment_program.append(" gl_FragColor = gl_FragColor.bgra;\n"); 821effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch break; 822effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch } 82390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 82490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 8255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) vertex_program = vertex_header + shared_variables + "void main() {\n" + 8265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) vertex_program + "}\n"; 8275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 8285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) fragment_program = fragment_directives + fragment_header + 8295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) shared_variables + "void main() {\n" + fragment_program + 8305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) "}\n"; 8315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 8325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) cache_entry->Setup(vertex_program.c_str(), fragment_program.c_str()); 83390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 83490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) return cache_entry; 83590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)} 83690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 8375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void ShaderProgram::Setup(const GLchar* vertex_shader_text, 8385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const GLchar* fragment_shader_text) { 83990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // Shaders to map the source texture to |dst_texture_|. 8405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) GLuint vertex_shader = 8415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) helper_->CompileShaderFromSource(vertex_shader_text, GL_VERTEX_SHADER); 8425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (vertex_shader == 0) 8435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return; 8445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 8455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gl_->AttachShader(program_, vertex_shader); 8465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gl_->DeleteShader(vertex_shader); 8475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 8485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) GLuint fragment_shader = helper_->CompileShaderFromSource( 8495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) fragment_shader_text, GL_FRAGMENT_SHADER); 8505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (fragment_shader == 0) 8515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return; 8525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gl_->AttachShader(program_, fragment_shader); 8535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gl_->DeleteShader(fragment_shader); 8545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 8555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gl_->LinkProgram(program_); 8565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 8575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) GLint link_status = 0; 8585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gl_->GetProgramiv(program_, GL_LINK_STATUS, &link_status); 8595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (!link_status) 8605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return; 8615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 8625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) position_location_ = gl_->GetAttribLocation(program_, "a_position"); 8635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) texcoord_location_ = gl_->GetAttribLocation(program_, "a_texcoord"); 8645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) texture_location_ = gl_->GetUniformLocation(program_, "s_texture"); 8655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) src_subrect_location_ = gl_->GetUniformLocation(program_, "src_subrect"); 8665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) src_pixelsize_location_ = gl_->GetUniformLocation(program_, "src_pixelsize"); 8675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) dst_pixelsize_location_ = gl_->GetUniformLocation(program_, "dst_pixelsize"); 8685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scaling_vector_location_ = 8695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gl_->GetUniformLocation(program_, "scaling_vector"); 8705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) color_weights_location_ = gl_->GetUniformLocation(program_, "color_weights"); 8715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return; 87290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)} 87390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 8745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void ShaderProgram::UseProgram(const gfx::Size& src_size, 8755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const gfx::Rect& src_subrect, 8765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const gfx::Size& dst_size, 8775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) bool scale_x, 8785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) bool flip_y, 8795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) GLfloat color_weights[4]) { 8805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gl_->UseProgram(program_); 8815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 8825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // OpenGL defines the last parameter to VertexAttribPointer as type 8835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // "const GLvoid*" even though it is actually an offset into the buffer 8845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // object's data store and not a pointer to the client's address space. 8855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const void* offsets[2] = { 8865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 0, reinterpret_cast<const void*>(2 * sizeof(GLfloat)) 8875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) }; 8885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 8895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gl_->VertexAttribPointer(position_location_, 8905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 2, 8915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) GL_FLOAT, 8925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) GL_FALSE, 8935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 4 * sizeof(GLfloat), 8945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) offsets[0]); 8955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gl_->EnableVertexAttribArray(position_location_); 8965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 8975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gl_->VertexAttribPointer(texcoord_location_, 8985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 2, 8995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) GL_FLOAT, 9005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) GL_FALSE, 9015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 4 * sizeof(GLfloat), 9025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) offsets[1]); 9035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gl_->EnableVertexAttribArray(texcoord_location_); 9045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 9055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gl_->Uniform1i(texture_location_, 0); 90690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 90790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // Convert |src_subrect| to texture coordinates. 90890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) GLfloat src_subrect_texcoord[] = { 9095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) static_cast<float>(src_subrect.x()) / src_size.width(), 9105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) static_cast<float>(src_subrect.y()) / src_size.height(), 9115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) static_cast<float>(src_subrect.width()) / src_size.width(), 9125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) static_cast<float>(src_subrect.height()) / src_size.height(), }; 91390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (flip_y) { 91490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) src_subrect_texcoord[1] += src_subrect_texcoord[3]; 91590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) src_subrect_texcoord[3] *= -1.0; 91690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 9175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gl_->Uniform4fv(src_subrect_location_, 1, src_subrect_texcoord); 9185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 9195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gl_->Uniform2f(src_pixelsize_location_, src_size.width(), src_size.height()); 9205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gl_->Uniform2f(dst_pixelsize_location_, 9215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) static_cast<float>(dst_size.width()), 9225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) static_cast<float>(dst_size.height())); 9235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 9245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gl_->Uniform2f( 9255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scaling_vector_location_, scale_x ? 1.0 : 0.0, scale_x ? 0.0 : 1.0); 9265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gl_->Uniform4fv(color_weights_location_, 1, color_weights); 92790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)} 92890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 92990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)} // namespace content 930