| /* |
| * Copyright 2010, Google Inc. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| /* |
| * This is a conversion of a cg shader from Chrome: |
| * http://src.chromium.org/viewvc/chrome/trunk/src/o3d/samples/shaders/yuv2rgb.shader |
| */ |
| |
| /* |
| * This shader takes a Y'UV420p image as a single greyscale plane, and |
| * converts it to RGB by sampling the correct parts of the image, and |
| * by converting the colorspace to RGB on the fly. |
| */ |
| |
| /* |
| * These represent the image dimensions of the SOURCE IMAGE (not the |
| * Y'UV420p image). This is the same as the dimensions of the Y' |
| * portion of the Y'UV420p image. They are set from JavaScript. |
| */ |
| uniform float imageWidth; |
| uniform float imageHeight; |
| |
| /* |
| * This is the texture sampler where the greyscale Y'UV420p image is |
| * accessed. |
| */ |
| uniform sampler2D textureSampler; |
| |
| #if defined (USE_UNIFORM_MATRIX) |
| uniform mat4 conversion; |
| #endif |
| |
| varying vec4 v1; |
| |
| /** |
| * This fetches an individual Y pixel from the image, given the current |
| * texture coordinates (which range from 0 to 1 on the source texture |
| * image). They are mapped to the portion of the image that contains |
| * the Y component. |
| * |
| * @param position This is the position of the main image that we're |
| * trying to render, in parametric coordinates. |
| */ |
| float getYPixel(vec2 position) { |
| position.y = 1. - (position.y * 2.0 / 3.0 + 1.0 / 3.0); |
| return texture2D(textureSampler, position).x; |
| } |
| |
| /** |
| * This does the crazy work of calculating the planar position (the |
| * position in the byte stream of the image) of the U or V pixel, and |
| * then converting that back to x and y coordinates, so that we can |
| * account for the fact that V is appended to U in the image, and the |
| * complications that causes (see below for a diagram). |
| * |
| * @param position This is the position of the main image that we're |
| * trying to render, in pixels. |
| * |
| * @param planarOffset This is an offset to add to the planar address |
| * we calculate so that we can find the U image after the V |
| * image. |
| */ |
| vec2 mapCommon(vec2 position, float planarOffset) { |
| planarOffset += imageWidth * floor(position.y / 2.0) / 2.0 + |
| floor((imageWidth - 1.0 - position.x) / 2.0); |
| float x = floor(imageWidth - 1.0 - floor(mod(planarOffset, imageWidth))); |
| float y = floor(planarOffset / imageWidth); |
| return vec2((x + 0.5) / imageWidth, 1. - (y + 0.5) / (1.5 * imageHeight)); |
| } |
| |
| /** |
| * This is a helper function for mapping pixel locations to a texture |
| * coordinate for the U plane. |
| * |
| * @param position This is the position of the main image that we're |
| * trying to render, in pixels. |
| */ |
| vec2 mapU(vec2 position) { |
| float planarOffset = (imageWidth * imageHeight) / 4.0; |
| return mapCommon(position, planarOffset); |
| } |
| |
| /** |
| * This is a helper function for mapping pixel locations to a texture |
| * coordinate for the V plane. |
| * |
| * @param position This is the position of the main image that we're |
| * trying to render, in pixels. |
| */ |
| vec2 mapV(vec2 position) { |
| return mapCommon(position, 0.0); |
| } |
| |
| /** |
| * Given the texture coordinates, our pixel shader grabs the right |
| * value from each channel of the source image, converts it from Y'UV |
| * to RGB, and returns the result. |
| * |
| * Each U and V pixel provides color information for a 2x2 block of Y |
| * pixels. The U and V planes are just appended to the Y image. |
| * |
| * For images that have a height divisible by 4, things work out nicely. |
| * For images that are merely divisible by 2, it's not so nice |
| * (and YUV420 doesn't work for image sizes not divisible by 2). |
| * |
| * Here is a 6x6 image, with the layout of the planes of U and V. |
| * Notice that the V plane starts halfway through the last scanline |
| * that has U on it. |
| * |
| * 1 +---+---+---+---+---+---+ |
| * | Y | Y | Y | Y | Y | Y | |
| * +---+---+---+---+---+---+ |
| * | Y | Y | Y | Y | Y | Y | |
| * +---+---+---+---+---+---+ |
| * | Y | Y | Y | Y | Y | Y | |
| * +---+---+---+---+---+---+ |
| * | Y | Y | Y | Y | Y | Y | |
| * +---+---+---+---+---+---+ |
| * | Y | Y | Y | Y | Y | Y | |
| * +---+---+---+---+---+---+ |
| * | Y | Y | Y | Y | Y | Y | |
| * .3 +---+---+---+---+---+---+ |
| * | U | U | U | U | U | U | |
| * +---+---+---+---+---+---+ |
| * | U | U | U | V | V | V | |
| * +---+---+---+---+---+---+ |
| * | V | V | V | V | V | V | |
| * 0 +---+---+---+---+---+---+ |
| * 0 1 |
| * |
| * Here is a 4x4 image, where the U and V planes are nicely split into |
| * separable blocks. |
| * |
| * 1 +---+---+---+---+ |
| * | Y | Y | Y | Y | |
| * +---+---+---+---+ |
| * | Y | Y | Y | Y | |
| * +---+---+---+---+ |
| * | Y | Y | Y | Y | |
| * +---+---+---+---+ |
| * | Y | Y | Y | Y | |
| * .3 +---+---+---+---+ |
| * | U | U | U | U | |
| * +---+---+---+---+ |
| * | V | V | V | V | |
| * 0 +---+---+---+---+ |
| * 0 1 |
| * |
| */ |
| void main() { |
| /* |
| * Calculate what image pixel we're on, since we have to calculate |
| * the location in the image stream, using floor in several places |
| * which makes it hard to use parametric coordinates. |
| */ |
| vec2 pixelPosition = vec2(floor(imageWidth * v1.x), |
| floor(imageHeight * v1.y)); |
| |
| /* |
| * We can use the parametric coordinates to get the Y channel, since it's |
| * a relatively normal image. |
| */ |
| float yChannel = getYPixel(vec2(v1)); |
| |
| /* |
| * As noted above, the U and V planes are smashed onto the end of |
| * the image in an odd way (in our 2D texture mapping, at least), so |
| * these mapping functions take care of that oddness. |
| */ |
| float uChannel = texture2D(textureSampler, mapU(pixelPosition)).x; |
| float vChannel = texture2D(textureSampler, mapV(pixelPosition)).x; |
| |
| /* |
| * This does the colorspace conversion from Y'UV to RGB as a matrix |
| * multiply. It also does the offset of the U and V channels from |
| * [0,1] to [-.5,.5] as part of the transform. |
| */ |
| vec4 channels = vec4(yChannel, uChannel, vChannel, 1.0); |
| #if !defined(USE_UNIFORM_MATRIX) |
| mat4 conversion = mat4( 1.0, 1.0, 1.0, 0.0, |
| 0.0, -0.344, 1.772, 0.0, |
| 1.402, -0.714, 0.0, 0.0, |
| -0.701, 0.529, -0.886, 1.0); |
| #endif |
| |
| gl_FragColor = conversion * channels; |
| } |