@Override
  public final void setTransform(/*@Nonnull*/ final float[] value, final boolean transpose) {

    Check.notNull(value, "Transform value cannot be null");
    Check.state(!orthoMode, "Must be in 3D mode");

    // Render any outstanding quads first
    if (!pipeline.isEmpty()) {
      fireEvent(EventType.AUTOMATIC_FLUSH);
      final GL gl = GLContext.getCurrentGL();
      flush(gl);
    }

    // Store the transform
    System.arraycopy(value, 0, this.transform, 0, value.length);
    this.transposed = transpose;

    // Change the transform
    if (inRenderCycle) {
      final GL gl = GLContext.getCurrentGL();
      doSetTransform3d(gl, value, transpose);
    } else {
      transformDirty = true;
    }
  }
  @Override
  public final void setColor(final float r, final float g, final float b, final float a) {

    // Check if already has the color
    if (hasColor(r, g, b, a)) {
      return;
    }

    // Render any outstanding quads first
    if (!pipeline.isEmpty()) {
      fireEvent(EventType.AUTOMATIC_FLUSH);
      final GL gl = GLContext.getCurrentGL();
      flush(gl);
    }

    // Store the color
    this.r = r;
    this.g = g;
    this.b = b;
    this.a = a;

    // Change the color
    if (inRenderCycle) {
      final GL gl = GLContext.getCurrentGL();
      doSetColor(gl, r, g, b, a);
    } else {
      colorDirty = true;
    }
  }
  @Override
  public final void drawTexture(
      final GL gl,
      Texture texture,
      final float x,
      float y,
      float z,
      int texturex,
      int texturey,
      int width,
      int height,
      float scale) {
    Check.notNull(gl, "GL cannot be null");
    Check.notNull(texture, "Texture cannot be null");

    final TextureCoords coords =
        texture.getSubImageTexCoords(texturex, texturey, texturex + width, texturey + height);

    // Compute position and size
    quad.xl = x;
    quad.xr = quad.xl + (scale * width);
    quad.yb = y;
    quad.yt = quad.yb + (scale * height);
    quad.z = z;
    quad.sl = coords.left();
    quad.sr = coords.right();
    quad.tb = coords.bottom();
    quad.tt = coords.top();

    // Draw quad
    pipeline.addQuad(gl, quad);
  }
  @Override
  public final void flush(/*@Nonnull*/ final GL gl) {

    Check.notNull(gl, "GL cannot be null");
    Check.state(inRenderCycle, "Must be in render cycle");

    pipeline.flush(gl);
    gl.glFlush();
  }
  @Override
  public final void dispose(/*@Nonnull*/ final GL gl) {

    Check.notNull(gl, "GL cannot be null");

    doDispose(gl);
    listeners.clear();
    pipeline.dispose(gl);
  }
  /**
   * Changes the quad pipeline.
   *
   * @param gl Current OpenGL context
   * @param pipeline Quad pipeline to change to
   */
  private final void setPipeline(
      /*@Nonnull*/ final GL gl, /*@Nonnull*/ final QuadPipeline pipeline) {

    assert gl != null : "GL should not be null";
    assert pipeline != null : "Pipeline should not be null";

    final QuadPipeline oldPipeline = this.pipeline;
    final QuadPipeline newPipeline = pipeline;

    // Remove the old pipeline
    if (oldPipeline != null) {
      oldPipeline.removeListener(this);
      oldPipeline.dispose(gl);
      this.pipeline = null;
    }

    // Store the new pipeline
    newPipeline.addListener(this);
    this.pipeline = newPipeline;
    pipelineDirty = false;
  }
  @Override
  public final void endRendering(/*@Nonnull*/ final GL gl) {

    Check.notNull(gl, "GL cannot be null");

    // Store text renderer state
    inRenderCycle = false;

    // Pass to quad renderer
    pipeline.endRendering(gl);

    // Perform hook
    doEndRendering(gl);
  }
  @Override
  public final void beginRendering(
      /*@Nonnull*/ final GL gl,
      final boolean ortho, /*@Nonnegative*/
      final int width, /*@Nonnegative*/
      final int height,
      final boolean disableDepthTest) {

    Check.notNull(gl, "GL cannot be null");
    Check.argument(width >= 0, "Width cannot be negative");
    Check.argument(height >= 0, "Height cannot be negative");

    // Perform hook
    doBeginRendering(gl, ortho, width, height, disableDepthTest);

    // Store text renderer state
    inRenderCycle = true;
    orthoMode = ortho;

    // Make sure the pipeline is made
    if (pipelineDirty) {
      setPipeline(gl, doCreateQuadPipeline(gl));
    }

    // Pass to quad renderer
    pipeline.beginRendering(gl);

    // Make sure color is correct
    if (colorDirty) {
      doSetColor(gl, r, g, b, a);
      colorDirty = false;
    }

    // Make sure transform is correct
    if (transformDirty) {
      doSetTransform3d(gl, transform, transposed);
      transformDirty = false;
    }
  }