Home | History | Annotate | Download | only in client
      1 // Copyright (c) 2012 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 "content/common/gpu/client/gl_helper_scaling.h"
      6 
      7 #include <deque>
      8 #include <string>
      9 #include <vector>
     10 
     11 #include "base/bind.h"
     12 #include "base/debug/trace_event.h"
     13 #include "base/lazy_instance.h"
     14 #include "base/logging.h"
     15 #include "base/memory/ref_counted.h"
     16 #include "base/message_loop/message_loop.h"
     17 #include "base/time/time.h"
     18 #include "gpu/command_buffer/client/gles2_interface.h"
     19 #include "third_party/skia/include/core/SkRegion.h"
     20 #include "ui/gfx/rect.h"
     21 #include "ui/gfx/size.h"
     22 
     23 using gpu::gles2::GLES2Interface;
     24 
     25 namespace content {
     26 
     27 GLHelperScaling::GLHelperScaling(GLES2Interface* gl, GLHelper* helper)
     28     : gl_(gl), helper_(helper), vertex_attributes_buffer_(gl_) {
     29   InitBuffer();
     30 }
     31 
     32 GLHelperScaling::~GLHelperScaling() {}
     33 
     34 // Used to keep track of a generated shader program. The program
     35 // is passed in as text through Setup and is used by calling
     36 // UseProgram() with the right parameters. Note that |gl_|
     37 // and |helper_| are assumed to live longer than this program.
     38 class ShaderProgram : public base::RefCounted<ShaderProgram> {
     39  public:
     40   ShaderProgram(GLES2Interface* gl, GLHelper* helper)
     41       : gl_(gl),
     42         helper_(helper),
     43         program_(gl_->CreateProgram()),
     44         position_location_(-1),
     45         texcoord_location_(-1),
     46         src_subrect_location_(-1),
     47         src_pixelsize_location_(-1),
     48         dst_pixelsize_location_(-1),
     49         scaling_vector_location_(-1),
     50         color_weights_location_(-1) {}
     51 
     52   // Compile shader program.
     53   void Setup(const GLchar* vertex_shader_text,
     54              const GLchar* fragment_shader_text);
     55 
     56   // UseProgram must be called with GL_TEXTURE_2D bound to the
     57   // source texture and GL_ARRAY_BUFFER bound to a vertex
     58   // attribute buffer.
     59   void UseProgram(const gfx::Size& src_size,
     60                   const gfx::Rect& src_subrect,
     61                   const gfx::Size& dst_size,
     62                   bool scale_x,
     63                   bool flip_y,
     64                   GLfloat color_weights[4]);
     65 
     66   bool Initialized() const { return position_location_ != -1; }
     67 
     68  private:
     69   friend class base::RefCounted<ShaderProgram>;
     70   ~ShaderProgram() { gl_->DeleteProgram(program_); }
     71 
     72   GLES2Interface* gl_;
     73   GLHelper* helper_;
     74 
     75   // A program for copying a source texture into a destination texture.
     76   GLuint program_;
     77 
     78   // The location of the position in the program.
     79   GLint position_location_;
     80   // The location of the texture coordinate in the program.
     81   GLint texcoord_location_;
     82   // The location of the source texture in the program.
     83   GLint texture_location_;
     84   // The location of the texture coordinate of
     85   // the sub-rectangle in the program.
     86   GLint src_subrect_location_;
     87   // Location of size of source image in pixels.
     88   GLint src_pixelsize_location_;
     89   // Location of size of destination image in pixels.
     90   GLint dst_pixelsize_location_;
     91   // Location of vector for scaling direction.
     92   GLint scaling_vector_location_;
     93   // Location of color weights.
     94   GLint color_weights_location_;
     95 
     96   DISALLOW_COPY_AND_ASSIGN(ShaderProgram);
     97 };
     98 
     99 // Implementation of a single stage in a scaler pipeline. If the pipeline has
    100 // multiple stages, it calls Scale() on the subscaler, then further scales the
    101 // output. Caches textures and framebuffers to avoid allocating/deleting
    102 // them once per frame, which can be expensive on some drivers.
    103 class ScalerImpl : public GLHelper::ScalerInterface,
    104                    public GLHelperScaling::ShaderInterface {
    105  public:
    106   // |gl| and |copy_impl| are expected to live longer than this object.
    107   // |src_size| is the size of the input texture in pixels.
    108   // |dst_size| is the size of the output texutre in pixels.
    109   // |src_subrect| is the portion of the src to copy to the output texture.
    110   // If |scale_x| is true, we are scaling along the X axis, otherwise Y.
    111   // If we are scaling in both X and Y, |scale_x| is ignored.
    112   // If |vertically_flip_texture| is true, output will be upside-down.
    113   // If |swizzle| is true, RGBA will be transformed into BGRA.
    114   // |color_weights| are only used together with SHADER_PLANAR to specify
    115   //   how to convert RGB colors into a single value.
    116   ScalerImpl(GLES2Interface* gl,
    117              GLHelperScaling* scaler_helper,
    118              const GLHelperScaling::ScalerStage& scaler_stage,
    119              ScalerImpl* subscaler,
    120              const float* color_weights)
    121       : gl_(gl),
    122         scaler_helper_(scaler_helper),
    123         spec_(scaler_stage),
    124         intermediate_texture_(0),
    125         dst_framebuffer_(gl),
    126         subscaler_(subscaler) {
    127     if (color_weights) {
    128       color_weights_[0] = color_weights[0];
    129       color_weights_[1] = color_weights[1];
    130       color_weights_[2] = color_weights[2];
    131       color_weights_[3] = color_weights[3];
    132     } else {
    133       color_weights_[0] = 0.0;
    134       color_weights_[1] = 0.0;
    135       color_weights_[2] = 0.0;
    136       color_weights_[3] = 0.0;
    137     }
    138     shader_program_ =
    139         scaler_helper_->GetShaderProgram(spec_.shader, spec_.swizzle);
    140 
    141     if (subscaler_) {
    142       intermediate_texture_ = 0u;
    143       gl_->GenTextures(1, &intermediate_texture_);
    144       ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_,
    145                                                         intermediate_texture_);
    146       gl_->TexImage2D(GL_TEXTURE_2D,
    147                       0,
    148                       GL_RGBA,
    149                       spec_.src_size.width(),
    150                       spec_.src_size.height(),
    151                       0,
    152                       GL_RGBA,
    153                       GL_UNSIGNED_BYTE,
    154                       NULL);
    155     }
    156   }
    157 
    158   virtual ~ScalerImpl() {
    159     if (intermediate_texture_) {
    160       gl_->DeleteTextures(1, &intermediate_texture_);
    161     }
    162   }
    163 
    164   // GLHelperShader::ShaderInterface implementation.
    165   virtual void Execute(GLuint source_texture,
    166                        const std::vector<GLuint>& dest_textures) OVERRIDE {
    167     if (subscaler_) {
    168       subscaler_->Scale(source_texture, intermediate_texture_);
    169       source_texture = intermediate_texture_;
    170     }
    171 
    172     ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(
    173         gl_, dst_framebuffer_);
    174     DCHECK_GT(dest_textures.size(), 0U);
    175     scoped_ptr<GLenum[]> buffers(new GLenum[dest_textures.size()]);
    176     for (size_t t = 0; t < dest_textures.size(); t++) {
    177       ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, dest_textures[t]);
    178       gl_->FramebufferTexture2D(GL_FRAMEBUFFER,
    179                                 GL_COLOR_ATTACHMENT0 + t,
    180                                 GL_TEXTURE_2D,
    181                                 dest_textures[t],
    182                                 0);
    183       buffers[t] = GL_COLOR_ATTACHMENT0 + t;
    184     }
    185     ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, source_texture);
    186 
    187     gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    188     gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    189     gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    190     gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    191 
    192     ScopedBufferBinder<GL_ARRAY_BUFFER> buffer_binder(
    193         gl_, scaler_helper_->vertex_attributes_buffer_);
    194     DCHECK(shader_program_->Initialized());
    195     shader_program_->UseProgram(spec_.src_size,
    196                                 spec_.src_subrect,
    197                                 spec_.dst_size,
    198                                 spec_.scale_x,
    199                                 spec_.vertically_flip_texture,
    200                                 color_weights_);
    201     gl_->Viewport(0, 0, spec_.dst_size.width(), spec_.dst_size.height());
    202 
    203     if (dest_textures.size() > 1) {
    204       DCHECK_LE(static_cast<int>(dest_textures.size()),
    205                 scaler_helper_->helper_->MaxDrawBuffers());
    206       gl_->DrawBuffersEXT(dest_textures.size(), buffers.get());
    207     }
    208     // Conduct texture mapping by drawing a quad composed of two triangles.
    209     gl_->DrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    210     if (dest_textures.size() > 1) {
    211       // Set the draw buffers back to not confuse others.
    212       gl_->DrawBuffersEXT(1, &buffers[0]);
    213     }
    214   }
    215 
    216   // GLHelper::ScalerInterface implementation.
    217   virtual void Scale(GLuint source_texture, GLuint dest_texture) OVERRIDE {
    218     std::vector<GLuint> tmp(1);
    219     tmp[0] = dest_texture;
    220     Execute(source_texture, tmp);
    221   }
    222 
    223   virtual const gfx::Size& SrcSize() OVERRIDE {
    224     if (subscaler_) {
    225       return subscaler_->SrcSize();
    226     }
    227     return spec_.src_size;
    228   }
    229   virtual const gfx::Rect& SrcSubrect() OVERRIDE {
    230     if (subscaler_) {
    231       return subscaler_->SrcSubrect();
    232     }
    233     return spec_.src_subrect;
    234   }
    235   virtual const gfx::Size& DstSize() OVERRIDE { return spec_.dst_size; }
    236 
    237  private:
    238   GLES2Interface* gl_;
    239   GLHelperScaling* scaler_helper_;
    240   GLHelperScaling::ScalerStage spec_;
    241   GLfloat color_weights_[4];
    242   GLuint intermediate_texture_;
    243   scoped_refptr<ShaderProgram> shader_program_;
    244   ScopedFramebuffer dst_framebuffer_;
    245   scoped_ptr<ScalerImpl> subscaler_;
    246 };
    247 
    248 GLHelperScaling::ScalerStage::ScalerStage(ShaderType shader_,
    249                                           gfx::Size src_size_,
    250                                           gfx::Rect src_subrect_,
    251                                           gfx::Size dst_size_,
    252                                           bool scale_x_,
    253                                           bool vertically_flip_texture_,
    254                                           bool swizzle_)
    255     : shader(shader_),
    256       src_size(src_size_),
    257       src_subrect(src_subrect_),
    258       dst_size(dst_size_),
    259       scale_x(scale_x_),
    260       vertically_flip_texture(vertically_flip_texture_),
    261       swizzle(swizzle_) {}
    262 
    263 // The important inputs for this function is |x_ops| and
    264 // |y_ops|. They represent scaling operations to be done
    265 // on an imag of size |src_size|. If |quality| is SCALER_QUALITY_BEST,
    266 // then we will interpret these scale operations literally and we'll
    267 // create one scaler stage for each ScaleOp.  However, if |quality|
    268 // is SCALER_QUALITY_GOOD, then we can do a whole bunch of optimizations
    269 // by combining two or more ScaleOps in to a single scaler stage.
    270 // Normally we process ScaleOps from |y_ops| first and |x_ops| after
    271 // all |y_ops| are processed, but sometimes we can combine one or more
    272 // operation from both queues essentially for free. This is the reason
    273 // why |x_ops| and |y_ops| aren't just one single queue.
    274 void GLHelperScaling::ConvertScalerOpsToScalerStages(
    275     GLHelper::ScalerQuality quality,
    276     gfx::Size src_size,
    277     gfx::Rect src_subrect,
    278     const gfx::Size& dst_size,
    279     bool vertically_flip_texture,
    280     bool swizzle,
    281     std::deque<GLHelperScaling::ScaleOp>* x_ops,
    282     std::deque<GLHelperScaling::ScaleOp>* y_ops,
    283     std::vector<ScalerStage>* scaler_stages) {
    284   while (!x_ops->empty() || !y_ops->empty()) {
    285     gfx::Size intermediate_size = src_subrect.size();
    286     std::deque<ScaleOp>* current_queue = NULL;
    287 
    288     if (!y_ops->empty()) {
    289       current_queue = y_ops;
    290     } else {
    291       current_queue = x_ops;
    292     }
    293 
    294     ShaderType current_shader = SHADER_BILINEAR;
    295     switch (current_queue->front().scale_factor) {
    296       case 0:
    297         if (quality == GLHelper::SCALER_QUALITY_BEST) {
    298           current_shader = SHADER_BICUBIC_UPSCALE;
    299         }
    300         break;
    301       case 2:
    302         if (quality == GLHelper::SCALER_QUALITY_BEST) {
    303           current_shader = SHADER_BICUBIC_HALF_1D;
    304         }
    305         break;
    306       case 3:
    307         DCHECK(quality != GLHelper::SCALER_QUALITY_BEST);
    308         current_shader = SHADER_BILINEAR3;
    309         break;
    310       default:
    311         NOTREACHED();
    312     }
    313     bool scale_x = current_queue->front().scale_x;
    314     current_queue->front().UpdateSize(&intermediate_size);
    315     current_queue->pop_front();
    316 
    317     // Optimization: Sometimes we can combine 2-4 scaling operations into
    318     // one operation.
    319     if (quality == GLHelper::SCALER_QUALITY_GOOD) {
    320       if (!current_queue->empty() && current_shader == SHADER_BILINEAR) {
    321         // Combine two steps in the same dimension.
    322         current_queue->front().UpdateSize(&intermediate_size);
    323         current_queue->pop_front();
    324         current_shader = SHADER_BILINEAR2;
    325         if (!current_queue->empty()) {
    326           // Combine three steps in the same dimension.
    327           current_queue->front().UpdateSize(&intermediate_size);
    328           current_queue->pop_front();
    329           current_shader = SHADER_BILINEAR4;
    330         }
    331       }
    332       // Check if we can combine some steps in the other dimension as well.
    333       // Since all shaders currently use GL_LINEAR, we can easily scale up
    334       // or scale down by exactly 2x at the same time as we do another
    335       // operation. Currently, the following mergers are supported:
    336       // * 1 bilinear Y-pass with 1 bilinear X-pass (up or down)
    337       // * 2 bilinear Y-passes with 2 bilinear X-passes
    338       // * 1 bilinear Y-pass with N bilinear X-pass
    339       // * N bilinear Y-passes with 1 bilinear X-pass (down only)
    340       // Measurements indicate that generalizing this for 3x3 and 4x4
    341       // makes it slower on some platforms, such as the Pixel.
    342       if (!scale_x && x_ops->size() > 0 && x_ops->front().scale_factor <= 2) {
    343         int x_passes = 0;
    344         if (current_shader == SHADER_BILINEAR2 && x_ops->size() >= 2) {
    345           // 2y + 2x passes
    346           x_passes = 2;
    347           current_shader = SHADER_BILINEAR2X2;
    348         } else if (current_shader == SHADER_BILINEAR) {
    349           // 1y + Nx passes
    350           scale_x = true;
    351           switch (x_ops->size()) {
    352             case 0:
    353               NOTREACHED();
    354             case 1:
    355               if (x_ops->front().scale_factor == 3) {
    356                 current_shader = SHADER_BILINEAR3;
    357               }
    358               x_passes = 1;
    359               break;
    360             case 2:
    361               x_passes = 2;
    362               current_shader = SHADER_BILINEAR2;
    363               break;
    364             default:
    365               x_passes = 3;
    366               current_shader = SHADER_BILINEAR4;
    367               break;
    368           }
    369         } else if (x_ops->front().scale_factor == 2) {
    370           // Ny + 1x-downscale
    371           x_passes = 1;
    372         }
    373 
    374         for (int i = 0; i < x_passes; i++) {
    375           x_ops->front().UpdateSize(&intermediate_size);
    376           x_ops->pop_front();
    377         }
    378       }
    379     }
    380 
    381     scaler_stages->push_back(ScalerStage(current_shader,
    382                                          src_size,
    383                                          src_subrect,
    384                                          intermediate_size,
    385                                          scale_x,
    386                                          vertically_flip_texture,
    387                                          swizzle));
    388     src_size = intermediate_size;
    389     src_subrect = gfx::Rect(intermediate_size);
    390     vertically_flip_texture = false;
    391     swizzle = false;
    392   }
    393 }
    394 
    395 void GLHelperScaling::ComputeScalerStages(
    396     GLHelper::ScalerQuality quality,
    397     const gfx::Size& src_size,
    398     const gfx::Rect& src_subrect,
    399     const gfx::Size& dst_size,
    400     bool vertically_flip_texture,
    401     bool swizzle,
    402     std::vector<ScalerStage>* scaler_stages) {
    403   if (quality == GLHelper::SCALER_QUALITY_FAST ||
    404       src_subrect.size() == dst_size) {
    405     scaler_stages->push_back(ScalerStage(SHADER_BILINEAR,
    406                                          src_size,
    407                                          src_subrect,
    408                                          dst_size,
    409                                          false,
    410                                          vertically_flip_texture,
    411                                          swizzle));
    412     return;
    413   }
    414 
    415   std::deque<GLHelperScaling::ScaleOp> x_ops, y_ops;
    416   GLHelperScaling::ScaleOp::AddOps(src_subrect.width(),
    417                                    dst_size.width(),
    418                                    true,
    419                                    quality == GLHelper::SCALER_QUALITY_GOOD,
    420                                    &x_ops);
    421   GLHelperScaling::ScaleOp::AddOps(src_subrect.height(),
    422                                    dst_size.height(),
    423                                    false,
    424                                    quality == GLHelper::SCALER_QUALITY_GOOD,
    425                                    &y_ops);
    426 
    427   ConvertScalerOpsToScalerStages(quality,
    428                                  src_size,
    429                                  src_subrect,
    430                                  dst_size,
    431                                  vertically_flip_texture,
    432                                  swizzle,
    433                                  &x_ops,
    434                                  &y_ops,
    435                                  scaler_stages);
    436 }
    437 
    438 GLHelper::ScalerInterface* GLHelperScaling::CreateScaler(
    439     GLHelper::ScalerQuality quality,
    440     gfx::Size src_size,
    441     gfx::Rect src_subrect,
    442     const gfx::Size& dst_size,
    443     bool vertically_flip_texture,
    444     bool swizzle) {
    445   std::vector<ScalerStage> scaler_stages;
    446   ComputeScalerStages(quality,
    447                       src_size,
    448                       src_subrect,
    449                       dst_size,
    450                       vertically_flip_texture,
    451                       swizzle,
    452                       &scaler_stages);
    453 
    454   ScalerImpl* ret = NULL;
    455   for (unsigned int i = 0; i < scaler_stages.size(); i++) {
    456     ret = new ScalerImpl(gl_, this, scaler_stages[i], ret, NULL);
    457   }
    458   return ret;
    459 }
    460 
    461 GLHelper::ScalerInterface* GLHelperScaling::CreatePlanarScaler(
    462     const gfx::Size& src_size,
    463     const gfx::Rect& src_subrect,
    464     const gfx::Size& dst_size,
    465     bool vertically_flip_texture,
    466     bool swizzle,
    467     const float color_weights[4]) {
    468   ScalerStage stage(SHADER_PLANAR,
    469                     src_size,
    470                     src_subrect,
    471                     dst_size,
    472                     true,
    473                     vertically_flip_texture,
    474                     swizzle);
    475   return new ScalerImpl(gl_, this, stage, NULL, color_weights);
    476 }
    477 
    478 GLHelperScaling::ShaderInterface* GLHelperScaling::CreateYuvMrtShader(
    479     const gfx::Size& src_size,
    480     const gfx::Rect& src_subrect,
    481     const gfx::Size& dst_size,
    482     bool vertically_flip_texture,
    483     bool swizzle,
    484     ShaderType shader) {
    485   DCHECK(shader == SHADER_YUV_MRT_PASS1 || shader == SHADER_YUV_MRT_PASS2);
    486   ScalerStage stage(shader,
    487                     src_size,
    488                     src_subrect,
    489                     dst_size,
    490                     true,
    491                     vertically_flip_texture,
    492                     swizzle);
    493   return new ScalerImpl(gl_, this, stage, NULL, NULL);
    494 }
    495 
    496 const GLfloat GLHelperScaling::kVertexAttributes[] = {
    497     -1.0f, -1.0f, 0.0f, 0.0f,     // vertex 0
    498     1.0f,  -1.0f, 1.0f, 0.0f,     // vertex 1
    499     -1.0f, 1.0f,  0.0f, 1.0f,     // vertex 2
    500     1.0f,  1.0f,  1.0f, 1.0f, };  // vertex 3
    501 
    502 void GLHelperScaling::InitBuffer() {
    503   ScopedBufferBinder<GL_ARRAY_BUFFER> buffer_binder(gl_,
    504                                                     vertex_attributes_buffer_);
    505   gl_->BufferData(GL_ARRAY_BUFFER,
    506                   sizeof(kVertexAttributes),
    507                   kVertexAttributes,
    508                   GL_STATIC_DRAW);
    509 }
    510 
    511 scoped_refptr<ShaderProgram> GLHelperScaling::GetShaderProgram(ShaderType type,
    512                                                                bool swizzle) {
    513   ShaderProgramKeyType key(type, swizzle);
    514   scoped_refptr<ShaderProgram>& cache_entry(shader_programs_[key]);
    515   if (!cache_entry.get()) {
    516     cache_entry = new ShaderProgram(gl_, helper_);
    517     std::basic_string<GLchar> vertex_program;
    518     std::basic_string<GLchar> fragment_program;
    519     std::basic_string<GLchar> vertex_header;
    520     std::basic_string<GLchar> fragment_directives;
    521     std::basic_string<GLchar> fragment_header;
    522     std::basic_string<GLchar> shared_variables;
    523 
    524     vertex_header.append(
    525         "precision highp float;\n"
    526         "attribute vec2 a_position;\n"
    527         "attribute vec2 a_texcoord;\n"
    528         "uniform vec4 src_subrect;\n");
    529 
    530     fragment_header.append(
    531         "precision mediump float;\n"
    532         "uniform sampler2D s_texture;\n");
    533 
    534     vertex_program.append(
    535         "  gl_Position = vec4(a_position, 0.0, 1.0);\n"
    536         "  vec2 texcoord = src_subrect.xy + a_texcoord * src_subrect.zw;\n");
    537 
    538     switch (type) {
    539       case SHADER_BILINEAR:
    540         shared_variables.append("varying vec2 v_texcoord;\n");
    541         vertex_program.append("  v_texcoord = texcoord;\n");
    542         fragment_program.append(
    543             "  gl_FragColor = texture2D(s_texture, v_texcoord);\n");
    544         break;
    545 
    546       case SHADER_BILINEAR2:
    547         // This is equivialent to two passes of the BILINEAR shader above.
    548         // It can be used to scale an image down 1.0x-2.0x in either dimension,
    549         // or exactly 4x.
    550         shared_variables.append(
    551             "varying vec4 v_texcoords;\n");  // 2 texcoords packed in one quad
    552         vertex_header.append(
    553             "uniform vec2 scaling_vector;\n"
    554             "uniform vec2 dst_pixelsize;\n");
    555         vertex_program.append(
    556             "  vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n"
    557             "  step /= 4.0;\n"
    558             "  v_texcoords.xy = texcoord + step;\n"
    559             "  v_texcoords.zw = texcoord - step;\n");
    560 
    561         fragment_program.append(
    562             "  gl_FragColor = (texture2D(s_texture, v_texcoords.xy) +\n"
    563             "                  texture2D(s_texture, v_texcoords.zw)) / 2.0;\n");
    564         break;
    565 
    566       case SHADER_BILINEAR3:
    567         // This is kind of like doing 1.5 passes of the BILINEAR shader.
    568         // It can be used to scale an image down 1.5x-3.0x, or exactly 6x.
    569         shared_variables.append(
    570             "varying vec4 v_texcoords1;\n"  // 2 texcoords packed in one quad
    571             "varying vec2 v_texcoords2;\n");
    572         vertex_header.append(
    573             "uniform vec2 scaling_vector;\n"
    574             "uniform vec2 dst_pixelsize;\n");
    575         vertex_program.append(
    576             "  vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n"
    577             "  step /= 3.0;\n"
    578             "  v_texcoords1.xy = texcoord + step;\n"
    579             "  v_texcoords1.zw = texcoord;\n"
    580             "  v_texcoords2 = texcoord - step;\n");
    581         fragment_program.append(
    582             "  gl_FragColor = (texture2D(s_texture, v_texcoords1.xy) +\n"
    583             "                  texture2D(s_texture, v_texcoords1.zw) +\n"
    584             "                  texture2D(s_texture, v_texcoords2)) / 3.0;\n");
    585         break;
    586 
    587       case SHADER_BILINEAR4:
    588         // This is equivialent to three passes of the BILINEAR shader above,
    589         // It can be used to scale an image down 2.0x-4.0x or exactly 8x.
    590         shared_variables.append("varying vec4 v_texcoords[2];\n");
    591         vertex_header.append(
    592             "uniform vec2 scaling_vector;\n"
    593             "uniform vec2 dst_pixelsize;\n");
    594         vertex_program.append(
    595             "  vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n"
    596             "  step /= 8.0;\n"
    597             "  v_texcoords[0].xy = texcoord - step * 3.0;\n"
    598             "  v_texcoords[0].zw = texcoord - step;\n"
    599             "  v_texcoords[1].xy = texcoord + step;\n"
    600             "  v_texcoords[1].zw = texcoord + step * 3.0;\n");
    601         fragment_program.append(
    602             "  gl_FragColor = (\n"
    603             "      texture2D(s_texture, v_texcoords[0].xy) +\n"
    604             "      texture2D(s_texture, v_texcoords[0].zw) +\n"
    605             "      texture2D(s_texture, v_texcoords[1].xy) +\n"
    606             "      texture2D(s_texture, v_texcoords[1].zw)) / 4.0;\n");
    607         break;
    608 
    609       case SHADER_BILINEAR2X2:
    610         // This is equivialent to four passes of the BILINEAR shader above.
    611         // Two in each dimension. It can be used to scale an image down
    612         // 1.0x-2.0x in both X and Y directions. Or, it could be used to
    613         // scale an image down by exactly 4x in both dimensions.
    614         shared_variables.append("varying vec4 v_texcoords[2];\n");
    615         vertex_header.append("uniform vec2 dst_pixelsize;\n");
    616         vertex_program.append(
    617             "  vec2 step = src_subrect.zw / 4.0 / dst_pixelsize;\n"
    618             "  v_texcoords[0].xy = texcoord + vec2(step.x, step.y);\n"
    619             "  v_texcoords[0].zw = texcoord + vec2(step.x, -step.y);\n"
    620             "  v_texcoords[1].xy = texcoord + vec2(-step.x, step.y);\n"
    621             "  v_texcoords[1].zw = texcoord + vec2(-step.x, -step.y);\n");
    622         fragment_program.append(
    623             "  gl_FragColor = (\n"
    624             "      texture2D(s_texture, v_texcoords[0].xy) +\n"
    625             "      texture2D(s_texture, v_texcoords[0].zw) +\n"
    626             "      texture2D(s_texture, v_texcoords[1].xy) +\n"
    627             "      texture2D(s_texture, v_texcoords[1].zw)) / 4.0;\n");
    628         break;
    629 
    630       case SHADER_BICUBIC_HALF_1D:
    631         // This scales down texture by exactly half in one dimension.
    632         // directions in one pass. We use bilinear lookup to reduce
    633         // the number of texture reads from 8 to 4
    634         shared_variables.append(
    635             "const float CenterDist = 99.0 / 140.0;\n"
    636             "const float LobeDist = 11.0 / 4.0;\n"
    637             "const float CenterWeight = 35.0 / 64.0;\n"
    638             "const float LobeWeight = -3.0 / 64.0;\n"
    639             "varying vec4 v_texcoords[2];\n");
    640         vertex_header.append(
    641             "uniform vec2 scaling_vector;\n"
    642             "uniform vec2 src_pixelsize;\n");
    643         vertex_program.append(
    644             "  vec2 step = src_subrect.zw * scaling_vector / src_pixelsize;\n"
    645             "  v_texcoords[0].xy = texcoord - LobeDist * step;\n"
    646             "  v_texcoords[0].zw = texcoord - CenterDist * step;\n"
    647             "  v_texcoords[1].xy = texcoord + CenterDist * step;\n"
    648             "  v_texcoords[1].zw = texcoord + LobeDist * step;\n");
    649         fragment_program.append(
    650             "  gl_FragColor = \n"
    651             // Lobe pixels
    652             "      (texture2D(s_texture, v_texcoords[0].xy) +\n"
    653             "       texture2D(s_texture, v_texcoords[1].zw)) *\n"
    654             "          LobeWeight +\n"
    655             // Center pixels
    656             "      (texture2D(s_texture, v_texcoords[0].zw) +\n"
    657             "       texture2D(s_texture, v_texcoords[1].xy)) *\n"
    658             "          CenterWeight;\n");
    659         break;
    660 
    661       case SHADER_BICUBIC_UPSCALE:
    662         // When scaling up, we need 4 texture reads, but we can
    663         // save some instructions because will know in which range of
    664         // the bicubic function each call call to the bicubic function
    665         // will be in.
    666         // Also, when sampling the bicubic function like this, the sum
    667         // is always exactly one, so we can skip normalization as well.
    668         shared_variables.append("varying vec2 v_texcoord;\n");
    669         vertex_program.append("  v_texcoord = texcoord;\n");
    670         fragment_header.append(
    671             "uniform vec2 src_pixelsize;\n"
    672             "uniform vec2 scaling_vector;\n"
    673             "const float a = -0.5;\n"
    674             // This function is equivialent to calling the bicubic
    675             // function with x-1, x, 1-x and 2-x
    676             // (assuming 0 <= x < 1)
    677             "vec4 filt4(float x) {\n"
    678             "  return vec4(x * x * x, x * x, x, 1) *\n"
    679             "         mat4(       a,      -2.0 * a,   a, 0.0,\n"
    680             "               a + 2.0,      -a - 3.0, 0.0, 1.0,\n"
    681             "              -a - 2.0, 3.0 + 2.0 * a,  -a, 0.0,\n"
    682             "                    -a,             a, 0.0, 0.0);\n"
    683             "}\n"
    684             "mat4 pixels_x(vec2 pos, vec2 step) {\n"
    685             "  return mat4(\n"
    686             "      texture2D(s_texture, pos - step),\n"
    687             "      texture2D(s_texture, pos),\n"
    688             "      texture2D(s_texture, pos + step),\n"
    689             "      texture2D(s_texture, pos + step * 2.0));\n"
    690             "}\n");
    691         fragment_program.append(
    692             "  vec2 pixel_pos = v_texcoord * src_pixelsize - \n"
    693             "      scaling_vector / 2.0;\n"
    694             "  float frac = fract(dot(pixel_pos, scaling_vector));\n"
    695             "  vec2 base = (floor(pixel_pos) + vec2(0.5)) / src_pixelsize;\n"
    696             "  vec2 step = scaling_vector / src_pixelsize;\n"
    697             "  gl_FragColor = pixels_x(base, step) * filt4(frac);\n");
    698         break;
    699 
    700       case SHADER_PLANAR:
    701         // Converts four RGBA pixels into one pixel. Each RGBA
    702         // pixel will be dot-multiplied with the color weights and
    703         // then placed into a component of the output. This is used to
    704         // convert RGBA textures into Y, U and V textures. We do this
    705         // because single-component textures are not renderable on all
    706         // architectures.
    707         shared_variables.append("varying vec4 v_texcoords[2];\n");
    708         vertex_header.append(
    709             "uniform vec2 scaling_vector;\n"
    710             "uniform vec2 dst_pixelsize;\n");
    711         vertex_program.append(
    712             "  vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n"
    713             "  step /= 4.0;\n"
    714             "  v_texcoords[0].xy = texcoord - step * 1.5;\n"
    715             "  v_texcoords[0].zw = texcoord - step * 0.5;\n"
    716             "  v_texcoords[1].xy = texcoord + step * 0.5;\n"
    717             "  v_texcoords[1].zw = texcoord + step * 1.5;\n");
    718         fragment_header.append("uniform vec4 color_weights;\n");
    719         fragment_program.append(
    720             "  gl_FragColor = color_weights * mat4(\n"
    721             "    vec4(texture2D(s_texture, v_texcoords[0].xy).rgb, 1.0),\n"
    722             "    vec4(texture2D(s_texture, v_texcoords[0].zw).rgb, 1.0),\n"
    723             "    vec4(texture2D(s_texture, v_texcoords[1].xy).rgb, 1.0),\n"
    724             "    vec4(texture2D(s_texture, v_texcoords[1].zw).rgb, 1.0));\n");
    725         break;
    726 
    727       case SHADER_YUV_MRT_PASS1:
    728         // RGB24 to YV12 in two passes; writing two 8888 targets each pass.
    729         //
    730         // YV12 is full-resolution luma and half-resolution blue/red chroma.
    731         //
    732         //                  (original)
    733         //    RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX
    734         //    RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX
    735         //    RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX
    736         //    RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX
    737         //    RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX
    738         //    RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX
    739         //      |
    740         //      |      (y plane)    (temporary)
    741         //      |      YYYY YYYY     UUVV UUVV
    742         //      +--> { YYYY YYYY  +  UUVV UUVV }
    743         //             YYYY YYYY     UUVV UUVV
    744         //   First     YYYY YYYY     UUVV UUVV
    745         //    pass     YYYY YYYY     UUVV UUVV
    746         //             YYYY YYYY     UUVV UUVV
    747         //                              |
    748         //                              |  (u plane) (v plane)
    749         //   Second                     |      UUUU   VVVV
    750         //     pass                     +--> { UUUU + VVVV }
    751         //                                     UUUU   VVVV
    752         //
    753         shared_variables.append("varying vec4 v_texcoords[2];\n");
    754         vertex_header.append(
    755             "uniform vec2 scaling_vector;\n"
    756             "uniform vec2 dst_pixelsize;\n");
    757         vertex_program.append(
    758             "  vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n"
    759             "  step /= 4.0;\n"
    760             "  v_texcoords[0].xy = texcoord - step * 1.5;\n"
    761             "  v_texcoords[0].zw = texcoord - step * 0.5;\n"
    762             "  v_texcoords[1].xy = texcoord + step * 0.5;\n"
    763             "  v_texcoords[1].zw = texcoord + step * 1.5;\n");
    764         fragment_directives.append("#extension GL_EXT_draw_buffers : enable\n");
    765         fragment_header.append(
    766             "const vec3 kRGBtoY = vec3(0.257, 0.504, 0.098);\n"
    767             "const float kYBias = 0.0625;\n"
    768             // Divide U and V by two to compensate for averaging below.
    769             "const vec3 kRGBtoU = vec3(-0.148, -0.291, 0.439) / 2.0;\n"
    770             "const vec3 kRGBtoV = vec3(0.439, -0.368, -0.071) / 2.0;\n"
    771             "const float kUVBias = 0.5;\n");
    772         fragment_program.append(
    773             "  vec3 pixel1 = texture2D(s_texture, v_texcoords[0].xy).rgb;\n"
    774             "  vec3 pixel2 = texture2D(s_texture, v_texcoords[0].zw).rgb;\n"
    775             "  vec3 pixel3 = texture2D(s_texture, v_texcoords[1].xy).rgb;\n"
    776             "  vec3 pixel4 = texture2D(s_texture, v_texcoords[1].zw).rgb;\n"
    777             "  vec3 pixel12 = pixel1 + pixel2;\n"
    778             "  vec3 pixel34 = pixel3 + pixel4;\n"
    779             "  gl_FragData[0] = vec4(dot(pixel1, kRGBtoY),\n"
    780             "                        dot(pixel2, kRGBtoY),\n"
    781             "                        dot(pixel3, kRGBtoY),\n"
    782             "                        dot(pixel4, kRGBtoY)) + kYBias;\n"
    783             "  gl_FragData[1] = vec4(dot(pixel12, kRGBtoU),\n"
    784             "                        dot(pixel34, kRGBtoU),\n"
    785             "                        dot(pixel12, kRGBtoV),\n"
    786             "                        dot(pixel34, kRGBtoV)) + kUVBias;\n");
    787         break;
    788 
    789       case SHADER_YUV_MRT_PASS2:
    790         // We're just sampling two pixels and unswizzling them.  There's
    791         // no need to do vertical scaling with math, since bilinear
    792         // interpolation in the sampler takes care of that.
    793         shared_variables.append("varying vec4 v_texcoords;\n");
    794         vertex_header.append(
    795             "uniform vec2 scaling_vector;\n"
    796             "uniform vec2 dst_pixelsize;\n");
    797         vertex_program.append(
    798             "  vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n"
    799             "  step /= 2.0;\n"
    800             "  v_texcoords.xy = texcoord - step * 0.5;\n"
    801             "  v_texcoords.zw = texcoord + step * 0.5;\n");
    802         fragment_directives.append("#extension GL_EXT_draw_buffers : enable\n");
    803         fragment_program.append(
    804             "  vec4 lo_uuvv = texture2D(s_texture, v_texcoords.xy);\n"
    805             "  vec4 hi_uuvv = texture2D(s_texture, v_texcoords.zw);\n"
    806             "  gl_FragData[0] = vec4(lo_uuvv.rg, hi_uuvv.rg);\n"
    807             "  gl_FragData[1] = vec4(lo_uuvv.ba, hi_uuvv.ba);\n");
    808         break;
    809     }
    810     if (swizzle) {
    811       switch(type) {
    812         case SHADER_YUV_MRT_PASS1:
    813           fragment_program.append("  gl_FragData[0] = gl_FragData[0].bgra;\n");
    814           break;
    815         case SHADER_YUV_MRT_PASS2:
    816           fragment_program.append("  gl_FragData[0] = gl_FragData[0].bgra;\n");
    817           fragment_program.append("  gl_FragData[1] = gl_FragData[1].bgra;\n");
    818           break;
    819         default:
    820           fragment_program.append("  gl_FragColor = gl_FragColor.bgra;\n");
    821           break;
    822       }
    823     }
    824 
    825     vertex_program = vertex_header + shared_variables + "void main() {\n" +
    826                      vertex_program + "}\n";
    827 
    828     fragment_program = fragment_directives + fragment_header +
    829                        shared_variables + "void main() {\n" + fragment_program +
    830                        "}\n";
    831 
    832     cache_entry->Setup(vertex_program.c_str(), fragment_program.c_str());
    833   }
    834   return cache_entry;
    835 }
    836 
    837 void ShaderProgram::Setup(const GLchar* vertex_shader_text,
    838                           const GLchar* fragment_shader_text) {
    839   // Shaders to map the source texture to |dst_texture_|.
    840   GLuint vertex_shader =
    841       helper_->CompileShaderFromSource(vertex_shader_text, GL_VERTEX_SHADER);
    842   if (vertex_shader == 0)
    843     return;
    844 
    845   gl_->AttachShader(program_, vertex_shader);
    846   gl_->DeleteShader(vertex_shader);
    847 
    848   GLuint fragment_shader = helper_->CompileShaderFromSource(
    849       fragment_shader_text, GL_FRAGMENT_SHADER);
    850   if (fragment_shader == 0)
    851     return;
    852   gl_->AttachShader(program_, fragment_shader);
    853   gl_->DeleteShader(fragment_shader);
    854 
    855   gl_->LinkProgram(program_);
    856 
    857   GLint link_status = 0;
    858   gl_->GetProgramiv(program_, GL_LINK_STATUS, &link_status);
    859   if (!link_status)
    860     return;
    861 
    862   position_location_ = gl_->GetAttribLocation(program_, "a_position");
    863   texcoord_location_ = gl_->GetAttribLocation(program_, "a_texcoord");
    864   texture_location_ = gl_->GetUniformLocation(program_, "s_texture");
    865   src_subrect_location_ = gl_->GetUniformLocation(program_, "src_subrect");
    866   src_pixelsize_location_ = gl_->GetUniformLocation(program_, "src_pixelsize");
    867   dst_pixelsize_location_ = gl_->GetUniformLocation(program_, "dst_pixelsize");
    868   scaling_vector_location_ =
    869       gl_->GetUniformLocation(program_, "scaling_vector");
    870   color_weights_location_ = gl_->GetUniformLocation(program_, "color_weights");
    871   return;
    872 }
    873 
    874 void ShaderProgram::UseProgram(const gfx::Size& src_size,
    875                                const gfx::Rect& src_subrect,
    876                                const gfx::Size& dst_size,
    877                                bool scale_x,
    878                                bool flip_y,
    879                                GLfloat color_weights[4]) {
    880   gl_->UseProgram(program_);
    881 
    882   // OpenGL defines the last parameter to VertexAttribPointer as type
    883   // "const GLvoid*" even though it is actually an offset into the buffer
    884   // object's data store and not a pointer to the client's address space.
    885   const void* offsets[2] = {
    886       0, reinterpret_cast<const void*>(2 * sizeof(GLfloat))
    887   };
    888 
    889   gl_->VertexAttribPointer(position_location_,
    890                            2,
    891                            GL_FLOAT,
    892                            GL_FALSE,
    893                            4 * sizeof(GLfloat),
    894                            offsets[0]);
    895   gl_->EnableVertexAttribArray(position_location_);
    896 
    897   gl_->VertexAttribPointer(texcoord_location_,
    898                            2,
    899                            GL_FLOAT,
    900                            GL_FALSE,
    901                            4 * sizeof(GLfloat),
    902                            offsets[1]);
    903   gl_->EnableVertexAttribArray(texcoord_location_);
    904 
    905   gl_->Uniform1i(texture_location_, 0);
    906 
    907   // Convert |src_subrect| to texture coordinates.
    908   GLfloat src_subrect_texcoord[] = {
    909       static_cast<float>(src_subrect.x()) / src_size.width(),
    910       static_cast<float>(src_subrect.y()) / src_size.height(),
    911       static_cast<float>(src_subrect.width()) / src_size.width(),
    912       static_cast<float>(src_subrect.height()) / src_size.height(), };
    913   if (flip_y) {
    914     src_subrect_texcoord[1] += src_subrect_texcoord[3];
    915     src_subrect_texcoord[3] *= -1.0;
    916   }
    917   gl_->Uniform4fv(src_subrect_location_, 1, src_subrect_texcoord);
    918 
    919   gl_->Uniform2f(src_pixelsize_location_, src_size.width(), src_size.height());
    920   gl_->Uniform2f(dst_pixelsize_location_,
    921                  static_cast<float>(dst_size.width()),
    922                  static_cast<float>(dst_size.height()));
    923 
    924   gl_->Uniform2f(
    925       scaling_vector_location_, scale_x ? 1.0 : 0.0, scale_x ? 0.0 : 1.0);
    926   gl_->Uniform4fv(color_weights_location_, 1, color_weights);
    927 }
    928 
    929 }  // namespace content
    930