String8 ProgramCache::generateFragmentShader(const ProgramDescription& description) {
    String8 shader;

    const bool blendFramebuffer = description.framebufferMode >= SkXfermode::kPlus_Mode;
    if (blendFramebuffer) {
        shader.append(gFS_Header_Extension_FramebufferFetch);
    }
    if (description.hasExternalTexture) {
        shader.append(gFS_Header_Extension_ExternalTexture);
    }

    shader.append(gFS_Header);

    // Varyings
    if (description.hasTexture || description.hasExternalTexture) {
        shader.append(gVS_Header_Varyings_HasTexture);
    }
    if (description.hasVertexAlpha) {
        shader.append(gVS_Header_Varyings_HasVertexAlpha);
    }
    if (description.hasColors) {
        shader.append(gVS_Header_Varyings_HasColors);
    }
    if (description.hasGradient) {
        shader.append(gVS_Header_Varyings_HasGradient[gradientIndex(description)]);
    }
    if (description.hasBitmap) {
        shader.append(gVS_Header_Varyings_HasBitmap);
    }
    if (description.hasRoundRectClip) {
        shader.append(gVS_Header_Varyings_HasRoundRectClip);
    }

    // Uniforms
    int modulateOp = MODULATE_OP_NO_MODULATE;
    const bool singleColor = !description.hasTexture && !description.hasExternalTexture &&
            !description.hasGradient && !description.hasBitmap;

    if (description.modulate || singleColor) {
        shader.append(gFS_Uniforms_Color);
        if (!singleColor) modulateOp = MODULATE_OP_MODULATE;
    }
    if (description.hasTexture) {
        shader.append(gFS_Uniforms_TextureSampler);
    } else if (description.hasExternalTexture) {
        shader.append(gFS_Uniforms_ExternalTextureSampler);
    }
    if (description.hasGradient) {
        shader.appendFormat(gFS_Uniforms_GradientSampler[description.isSimpleGradient],
                gFS_Uniforms_Dither);
    }
    if (description.hasGammaCorrection) {
        shader.append(gFS_Uniforms_Gamma);
    }
    if (description.hasRoundRectClip) {
        shader.append(gFS_Uniforms_HasRoundRectClip);
    }

    // Optimization for common cases
    if (!description.hasVertexAlpha
            && !blendFramebuffer
            && !description.hasColors
            && description.colorOp == ProgramDescription::kColorNone
            && !description.hasDebugHighlight
            && !description.emulateStencil
            && !description.hasRoundRectClip) {
        bool fast = false;

        const bool noShader = !description.hasGradient && !description.hasBitmap;
        const bool singleTexture = (description.hasTexture || description.hasExternalTexture) &&
                !description.hasAlpha8Texture && noShader;
        const bool singleA8Texture = description.hasTexture &&
                description.hasAlpha8Texture && noShader;
        const bool singleGradient = !description.hasTexture && !description.hasExternalTexture &&
                description.hasGradient && !description.hasBitmap &&
                description.gradientType == ProgramDescription::kGradientLinear;

        if (singleColor) {
            shader.append(gFS_Fast_SingleColor);
            fast = true;
        } else if (singleTexture) {
            if (!description.modulate) {
                shader.append(gFS_Fast_SingleTexture);
            } else {
                shader.append(gFS_Fast_SingleModulateTexture);
            }
            fast = true;
        } else if (singleA8Texture) {
            if (!description.modulate) {
                if (description.hasGammaCorrection) {
                    shader.append(gFS_Fast_SingleA8Texture_ApplyGamma);
                } else {
                    shader.append(gFS_Fast_SingleA8Texture);
                }
            } else {
                if (description.hasGammaCorrection) {
                    shader.append(gFS_Fast_SingleModulateA8Texture_ApplyGamma);
                } else {
                    shader.append(gFS_Fast_SingleModulateA8Texture);
                }
            }
            fast = true;
        } else if (singleGradient) {
            if (!description.modulate) {
                shader.appendFormat(gFS_Fast_SingleGradient[description.isSimpleGradient],
                        gFS_Main_Dither[mHasES3]);
            } else {
                shader.appendFormat(gFS_Fast_SingleModulateGradient[description.isSimpleGradient],
                        gFS_Main_Dither[mHasES3]);
            }
            fast = true;
        }

        if (fast) {
#if DEBUG_PROGRAMS
                PROGRAM_LOGD("*** Fast case:\n");
                PROGRAM_LOGD("*** Generated fragment shader:\n\n");
                printLongString(shader);
#endif

            return shader;
        }
    }

    if (description.hasBitmap) {
        shader.append(gFS_Uniforms_BitmapSampler);
    }
    shader.append(gFS_Uniforms_ColorOp[description.colorOp]);

    // Generate required functions
    if (description.hasGradient && description.hasBitmap) {
        generateBlend(shader, "blendShaders", description.shadersMode);
    }
    if (description.colorOp == ProgramDescription::kColorBlend) {
        generateBlend(shader, "blendColors", description.colorMode);
    }
    if (blendFramebuffer) {
        generateBlend(shader, "blendFramebuffer", description.framebufferMode);
    }
    if (description.isBitmapNpot) {
        generateTextureWrap(shader, description.bitmapWrapS, description.bitmapWrapT);
    }

    // Begin the shader
    shader.append(gFS_Main); {
        if (description.emulateStencil) {
            shader.append(gFS_Main_EmulateStencil);
        }
        // Stores the result in fragColor directly
        if (description.hasTexture || description.hasExternalTexture) {
            if (description.hasAlpha8Texture) {
                if (!description.hasGradient && !description.hasBitmap) {
                    shader.append(gFS_Main_FetchA8Texture[modulateOp * 2 +
                                                          description.hasGammaCorrection]);
                }
            } else {
                shader.append(gFS_Main_FetchTexture[modulateOp]);
            }
        } else {
            if (!description.hasGradient && !description.hasBitmap) {
                shader.append(gFS_Main_FetchColor);
            }
        }
        if (description.hasGradient) {
            shader.append(gFS_Main_FetchGradient[gradientIndex(description)]);
            shader.appendFormat(gFS_Main_AddDitherToGradient, gFS_Main_Dither[mHasES3]);
        }
        if (description.hasBitmap) {
            if (!description.isBitmapNpot) {
                shader.append(gFS_Main_FetchBitmap);
            } else {
                shader.append(gFS_Main_FetchBitmapNpot);
            }
        }
        bool applyModulate = false;
        // Case when we have two shaders set
        if (description.hasGradient && description.hasBitmap) {
            if (description.isBitmapFirst) {
                shader.append(gFS_Main_BlendShadersBG);
            } else {
                shader.append(gFS_Main_BlendShadersGB);
            }
            applyModulate = shaderOp(description, shader, modulateOp,
                    gFS_Main_BlendShaders_Modulate);
        } else {
            if (description.hasGradient) {
                applyModulate = shaderOp(description, shader, modulateOp,
                        gFS_Main_GradientShader_Modulate);
            } else if (description.hasBitmap) {
                applyModulate = shaderOp(description, shader, modulateOp,
                        gFS_Main_BitmapShader_Modulate);
            }
        }

        if (description.modulate && applyModulate) {
            shader.append(gFS_Main_ModulateColor);
        }

        // Apply the color op if needed
        shader.append(gFS_Main_ApplyColorOp[description.colorOp]);

        if (description.hasVertexAlpha) {
            if (description.useShadowAlphaInterp) {
                shader.append(gFS_Main_ApplyVertexAlphaShadowInterp);
            } else {
                shader.append(gFS_Main_ApplyVertexAlphaLinearInterp);
            }
        }

        // Output the fragment
        if (!blendFramebuffer) {
            shader.append(gFS_Main_FragColor);
        } else {
            shader.append(!description.swapSrcDst ?
                    gFS_Main_FragColor_Blend : gFS_Main_FragColor_Blend_Swap);
        }
        if (description.hasColors) {
            shader.append(gFS_Main_FragColor_HasColors);
        }
        if (description.hasRoundRectClip) {
            shader.append(gFS_Main_FragColor_HasRoundRectClip);
        }
        if (description.hasDebugHighlight) {
            shader.append(gFS_Main_DebugHighlight);
        }
    }
    if (description.emulateStencil) {
        shader.append(gFS_Footer_EmulateStencil);
    }
    // End the shader
    shader.append(gFS_Footer);

#if DEBUG_PROGRAMS
        PROGRAM_LOGD("*** Generated fragment shader:\n\n");
        printLongString(shader);
#endif

    return shader;
}
String8 ProgramCache::generateFragmentShader(const ProgramDescription& description) {
    String8 shader;

    const bool blendFramebuffer = description.framebufferMode >= SkXfermode::kPlus_Mode;
    if (blendFramebuffer) {
        shader.append(gFS_Header_Extension_FramebufferFetch);
    }
    if (description.hasExternalTexture) {
        shader.append(gFS_Header_Extension_ExternalTexture);
    }

    shader.append(gFS_Header);

    // Varyings
    if (description.hasTexture || description.hasExternalTexture) {
        shader.append(gVS_Header_Varyings_HasTexture);
    }
    if (description.isAA) {
        shader.append(gVS_Header_Varyings_IsAA);
    }
    if (description.hasGradient) {
        shader.append(gVS_Header_Varyings_HasGradient[description.gradientType]);
    }
    if (description.hasBitmap) {
        int index = Caches::getInstance().extensions.needsHighpTexCoords() ? 1 : 0;
        shader.append(description.isPoint ?
                gVS_Header_Varyings_PointHasBitmap[index] :
                gVS_Header_Varyings_HasBitmap[index]);
    }

    // Uniforms
    int modulateOp = MODULATE_OP_NO_MODULATE;
    const bool singleColor = !description.hasTexture && !description.hasExternalTexture &&
            !description.hasGradient && !description.hasBitmap;

    if (description.modulate || singleColor) {
        shader.append(gFS_Uniforms_Color);
        if (!singleColor) modulateOp = MODULATE_OP_MODULATE;
    }
    if (description.hasTexture) {
        shader.append(gFS_Uniforms_TextureSampler);
    } else if (description.hasExternalTexture) {
        shader.append(gFS_Uniforms_ExternalTextureSampler);
    }
    if (description.isAA) {
        shader.append(gFS_Uniforms_AA);
    }
    if (description.hasGradient) {
        shader.append(gFS_Uniforms_GradientSampler[description.gradientType]);
    }
    if (description.hasBitmap && description.isPoint) {
        shader.append(gFS_Header_Uniforms_PointHasBitmap);
    }

    // Optimization for common cases
    if (!description.isAA && !blendFramebuffer &&
            description.colorOp == ProgramDescription::kColorNone && !description.isPoint) {
        bool fast = false;

        const bool noShader = !description.hasGradient && !description.hasBitmap;
        const bool singleTexture = (description.hasTexture || description.hasExternalTexture) &&
                !description.hasAlpha8Texture && noShader;
        const bool singleA8Texture = description.hasTexture &&
                description.hasAlpha8Texture && noShader;
        const bool singleGradient = !description.hasTexture && !description.hasExternalTexture &&
                description.hasGradient && !description.hasBitmap &&
                description.gradientType == ProgramDescription::kGradientLinear;

        if (singleColor) {
            shader.append(gFS_Fast_SingleColor);
            fast = true;
        } else if (singleTexture) {
            if (!description.modulate) {
                shader.append(gFS_Fast_SingleTexture);
            } else {
                shader.append(gFS_Fast_SingleModulateTexture);
            }
            fast = true;
        } else if (singleA8Texture) {
            if (!description.modulate) {
                shader.append(gFS_Fast_SingleA8Texture);
            } else {
                shader.append(gFS_Fast_SingleModulateA8Texture);
            }
            fast = true;
        } else if (singleGradient) {
            if (!description.modulate) {
                shader.append(gFS_Fast_SingleGradient);
            } else {
                shader.append(gFS_Fast_SingleModulateGradient);
            }
            fast = true;
        }

        if (fast) {
#if DEBUG_PROGRAMS
                PROGRAM_LOGD("*** Fast case:\n");
                PROGRAM_LOGD("*** Generated fragment shader:\n\n");
                printLongString(shader);
#endif

            return shader;
        }
    }

    if (description.hasBitmap) {
        shader.append(gFS_Uniforms_BitmapSampler);
    }
    shader.append(gFS_Uniforms_ColorOp[description.colorOp]);

    // Generate required functions
    if (description.hasGradient && description.hasBitmap) {
        generateBlend(shader, "blendShaders", description.shadersMode);
    }
    if (description.colorOp == ProgramDescription::kColorBlend) {
        generateBlend(shader, "blendColors", description.colorMode);
    }
    if (blendFramebuffer) {
        generateBlend(shader, "blendFramebuffer", description.framebufferMode);
    }
    if (description.isBitmapNpot) {
        generateTextureWrap(shader, description.bitmapWrapS, description.bitmapWrapT);
    }

    // Begin the shader
    shader.append(gFS_Main); {
        // Stores the result in fragColor directly
        if (description.hasTexture || description.hasExternalTexture) {
            if (description.hasAlpha8Texture) {
                if (!description.hasGradient && !description.hasBitmap) {
                    shader.append(gFS_Main_FetchA8Texture[modulateOp]);
                }
            } else {
                shader.append(gFS_Main_FetchTexture[modulateOp]);
            }
        } else {
            if ((!description.hasGradient && !description.hasBitmap) || description.modulate) {
                shader.append(gFS_Main_FetchColor);
            }
        }
        if (description.isAA) {
            shader.append(gFS_Main_AccountForAA);
        }
        if (description.hasGradient) {
            shader.append(gFS_Main_FetchGradient[description.gradientType]);
        }
        if (description.hasBitmap) {
            if (description.isPoint) {
                shader.append(gFS_Main_PointBitmapTexCoords);
            }
            if (!description.isBitmapNpot) {
                shader.append(gFS_Main_FetchBitmap);
            } else {
                shader.append(gFS_Main_FetchBitmapNpot);
            }
        }
        bool applyModulate = false;
        // Case when we have two shaders set
        if (description.hasGradient && description.hasBitmap) {
            int op = description.hasAlpha8Texture ? MODULATE_OP_MODULATE_A8 : modulateOp;
            if (description.isBitmapFirst) {
                shader.append(gFS_Main_BlendShadersBG);
            } else {
                shader.append(gFS_Main_BlendShadersGB);
            }
            shader.append(gFS_Main_BlendShaders_Modulate[op]);
            applyModulate = true;
        } else {
            if (description.hasGradient) {
                int op = description.hasAlpha8Texture ? MODULATE_OP_MODULATE_A8 : modulateOp;
                shader.append(gFS_Main_GradientShader_Modulate[op]);
                applyModulate = true;
            } else if (description.hasBitmap) {
                int op = description.hasAlpha8Texture ? MODULATE_OP_MODULATE_A8 : modulateOp;
                shader.append(gFS_Main_BitmapShader_Modulate[op]);
                applyModulate = true;
            }
        }
        if (description.modulate && applyModulate) {
            shader.append(gFS_Main_ModulateColor);
        }
        // Apply the color op if needed
        shader.append(gFS_Main_ApplyColorOp[description.colorOp]);
        // Output the fragment
        if (!blendFramebuffer) {
            shader.append(gFS_Main_FragColor);
        } else {
            shader.append(!description.swapSrcDst ?
                    gFS_Main_FragColor_Blend : gFS_Main_FragColor_Blend_Swap);
        }
    }
    // End the shader
    shader.append(gFS_Footer);

#if DEBUG_PROGRAMS
        PROGRAM_LOGD("*** Generated fragment shader:\n\n");
        printLongString(shader);
#endif

    return shader;
}