Support CHROMIUM_path_rendering

This is partial support for CHROMIUM_path_rendering
and implements basic path management and non-instanced
rendering.

BUG=angleproject:1382

Change-Id: I9c0e88183e0a915d522889323933439d25b45b5f
Reviewed-on: https://chromium-review.googlesource.com/348630
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Commit-Queue: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/validationES2.cpp b/src/libANGLE/validationES2.cpp
index 9dd1eb0..30c6c74 100644
--- a/src/libANGLE/validationES2.cpp
+++ b/src/libANGLE/validationES2.cpp
@@ -7,6 +7,9 @@
 // validationES2.cpp: Validation functions for OpenGL ES 2.0 entry point parameters
 
 #include "libANGLE/validationES2.h"
+
+#include <cstdint>
+
 #include "libANGLE/validationES.h"
 #include "libANGLE/validationES3.h"
 #include "libANGLE/Context.h"
@@ -2091,4 +2094,462 @@
     return true;
 }
 
+// CHROMIUM_path_rendering
+
+bool ValidateMatrix(Context *context, GLenum matrixMode, const GLfloat *matrix)
+{
+    if (!context->getExtensions().pathRendering)
+    {
+        context->handleError(
+            Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
+        return false;
+    }
+    if (matrixMode != GL_PATH_MODELVIEW_CHROMIUM && matrixMode != GL_PATH_PROJECTION_CHROMIUM)
+    {
+        context->handleError(Error(GL_INVALID_ENUM, "Invalid matrix mode."));
+        return false;
+    }
+    if (matrix == nullptr)
+    {
+        context->handleError(Error(GL_INVALID_OPERATION, "Invalid matrix."));
+        return false;
+    }
+    return true;
+}
+
+bool ValidateMatrixMode(Context *context, GLenum matrixMode)
+{
+    if (!context->getExtensions().pathRendering)
+    {
+        context->handleError(
+            Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
+        return false;
+    }
+    if (matrixMode != GL_PATH_MODELVIEW_CHROMIUM && matrixMode != GL_PATH_PROJECTION_CHROMIUM)
+    {
+        context->handleError(Error(GL_INVALID_ENUM, "Invalid matrix mode."));
+        return false;
+    }
+    return true;
+}
+
+bool ValidateGenPaths(Context *context, GLsizei range)
+{
+    if (!context->getExtensions().pathRendering)
+    {
+        context->handleError(
+            Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
+        return false;
+    }
+
+    // range = 0 is undefined in NV_path_rendering.
+    // we add stricter semantic check here and require a non zero positive range.
+    if (range <= 0)
+    {
+        context->handleError(Error(GL_INVALID_VALUE, "Invalid range."));
+        return false;
+    }
+
+    if (!angle::IsValueInRangeForNumericType<std::uint32_t>(range))
+    {
+        context->handleError(Error(GL_INVALID_OPERATION, "Range overflow."));
+        return false;
+    }
+
+    return true;
+}
+
+bool ValidateDeletePaths(Context *context, GLuint path, GLsizei range)
+{
+    if (!context->getExtensions().pathRendering)
+    {
+        context->handleError(
+            Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
+        return false;
+    }
+
+    // range = 0 is undefined in NV_path_rendering.
+    // we add stricter semantic check here and require a non zero positive range.
+    if (range <= 0)
+    {
+        context->handleError(Error(GL_INVALID_VALUE, "Invalid range."));
+        return false;
+    }
+
+    angle::CheckedNumeric<std::uint32_t> checkedRange(path);
+    checkedRange += range;
+
+    if (!angle::IsValueInRangeForNumericType<std::uint32_t>(range) || !checkedRange.IsValid())
+    {
+        context->handleError(Error(GL_INVALID_OPERATION, "Range overflow."));
+        return false;
+    }
+    return true;
+}
+
+bool ValidatePathCommands(Context *context,
+                          GLuint path,
+                          GLsizei numCommands,
+                          const GLubyte *commands,
+                          GLsizei numCoords,
+                          GLenum coordType,
+                          const void *coords)
+{
+    if (!context->getExtensions().pathRendering)
+    {
+        context->handleError(
+            Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
+        return false;
+    }
+    if (!context->hasPath(path))
+    {
+        context->handleError(Error(GL_INVALID_OPERATION, "No such path object."));
+        return false;
+    }
+
+    if (numCommands < 0)
+    {
+        context->handleError(Error(GL_INVALID_VALUE, "Invalid number of commands."));
+        return false;
+    }
+    else if (numCommands > 0)
+    {
+        if (!commands)
+        {
+            context->handleError(Error(GL_INVALID_VALUE, "No commands array given."));
+            return false;
+        }
+    }
+
+    if (numCoords < 0)
+    {
+        context->handleError(Error(GL_INVALID_VALUE, "Invalid number of coordinates."));
+        return false;
+    }
+    else if (numCoords > 0)
+    {
+        if (!coords)
+        {
+            context->handleError(Error(GL_INVALID_VALUE, "No coordinate array given."));
+            return false;
+        }
+    }
+
+    std::uint32_t coordTypeSize = 0;
+    switch (coordType)
+    {
+        case GL_BYTE:
+            coordTypeSize = sizeof(GLbyte);
+            break;
+
+        case GL_UNSIGNED_BYTE:
+            coordTypeSize = sizeof(GLubyte);
+            break;
+
+        case GL_SHORT:
+            coordTypeSize = sizeof(GLshort);
+            break;
+
+        case GL_UNSIGNED_SHORT:
+            coordTypeSize = sizeof(GLushort);
+            break;
+
+        case GL_FLOAT:
+            coordTypeSize = sizeof(GLfloat);
+            break;
+
+        default:
+            context->handleError(Error(GL_INVALID_ENUM, "Invalid coordinate type."));
+            return false;
+    }
+
+    angle::CheckedNumeric<std::uint32_t> checkedSize(numCommands);
+    checkedSize += (coordTypeSize * numCoords);
+    if (!checkedSize.IsValid())
+    {
+        context->handleError(Error(GL_INVALID_OPERATION, "Coord size overflow."));
+        return false;
+    }
+
+    // early return skips command data validation when it doesn't exist.
+    if (!commands)
+        return true;
+
+    GLsizei expectedNumCoords = 0;
+    for (GLsizei i = 0; i < numCommands; ++i)
+    {
+        switch (commands[i])
+        {
+            case GL_CLOSE_PATH_CHROMIUM:  // no coordinates.
+                break;
+            case GL_MOVE_TO_CHROMIUM:
+            case GL_LINE_TO_CHROMIUM:
+                expectedNumCoords += 2;
+                break;
+            case GL_QUADRATIC_CURVE_TO_CHROMIUM:
+                expectedNumCoords += 4;
+                break;
+            case GL_CUBIC_CURVE_TO_CHROMIUM:
+                expectedNumCoords += 6;
+                break;
+            case GL_CONIC_CURVE_TO_CHROMIUM:
+                expectedNumCoords += 5;
+                break;
+            default:
+                context->handleError(Error(GL_INVALID_ENUM, "Invalid command."));
+                return false;
+        }
+    }
+    if (expectedNumCoords != numCoords)
+    {
+        context->handleError(Error(GL_INVALID_VALUE, "Invalid number of coordinates."));
+        return false;
+    }
+
+    return true;
+}
+
+bool ValidateSetPathParameter(Context *context, GLuint path, GLenum pname, GLfloat value)
+{
+    if (!context->getExtensions().pathRendering)
+    {
+        context->handleError(
+            Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
+        return false;
+    }
+    if (!context->hasPath(path))
+    {
+        context->handleError(Error(GL_INVALID_OPERATION, "No such path object."));
+        return false;
+    }
+
+    switch (pname)
+    {
+        case GL_PATH_STROKE_WIDTH_CHROMIUM:
+            if (value < 0.0f)
+            {
+                context->handleError(Error(GL_INVALID_VALUE, "Invalid stroke width."));
+                return false;
+            }
+            break;
+        case GL_PATH_END_CAPS_CHROMIUM:
+            switch (static_cast<GLenum>(value))
+            {
+                case GL_FLAT_CHROMIUM:
+                case GL_SQUARE_CHROMIUM:
+                case GL_ROUND_CHROMIUM:
+                    break;
+                default:
+                    context->handleError(Error(GL_INVALID_ENUM, "Invalid end caps."));
+                    return false;
+            }
+            break;
+        case GL_PATH_JOIN_STYLE_CHROMIUM:
+            switch (static_cast<GLenum>(value))
+            {
+                case GL_MITER_REVERT_CHROMIUM:
+                case GL_BEVEL_CHROMIUM:
+                case GL_ROUND_CHROMIUM:
+                    break;
+                default:
+                    context->handleError(Error(GL_INVALID_ENUM, "Invalid join style."));
+                    return false;
+            }
+        case GL_PATH_MITER_LIMIT_CHROMIUM:
+            if (value < 0.0f)
+            {
+                context->handleError(Error(GL_INVALID_VALUE, "Invalid miter limit."));
+                return false;
+            }
+            break;
+
+        case GL_PATH_STROKE_BOUND_CHROMIUM:
+            // no errors, only clamping.
+            break;
+
+        default:
+            context->handleError(Error(GL_INVALID_ENUM, "Invalid path parameter."));
+            return false;
+    }
+    return true;
+}
+
+bool ValidateGetPathParameter(Context *context, GLuint path, GLenum pname, GLfloat *value)
+{
+    if (!context->getExtensions().pathRendering)
+    {
+        context->handleError(
+            Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
+        return false;
+    }
+
+    if (!context->hasPath(path))
+    {
+        context->handleError(Error(GL_INVALID_OPERATION, "No such path object."));
+        return false;
+    }
+    if (!value)
+    {
+        context->handleError(Error(GL_INVALID_VALUE, "No value array."));
+        return false;
+    }
+
+    switch (pname)
+    {
+        case GL_PATH_STROKE_WIDTH_CHROMIUM:
+        case GL_PATH_END_CAPS_CHROMIUM:
+        case GL_PATH_JOIN_STYLE_CHROMIUM:
+        case GL_PATH_MITER_LIMIT_CHROMIUM:
+        case GL_PATH_STROKE_BOUND_CHROMIUM:
+            break;
+
+        default:
+            context->handleError(Error(GL_INVALID_ENUM, "Invalid path parameter."));
+            return false;
+    }
+
+    return true;
+}
+
+bool ValidatePathStencilFunc(Context *context, GLenum func, GLint ref, GLuint mask)
+{
+    if (!context->getExtensions().pathRendering)
+    {
+        context->handleError(
+            Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
+        return false;
+    }
+
+    switch (func)
+    {
+        case GL_NEVER:
+        case GL_ALWAYS:
+        case GL_LESS:
+        case GL_LEQUAL:
+        case GL_EQUAL:
+        case GL_GEQUAL:
+        case GL_GREATER:
+        case GL_NOTEQUAL:
+            break;
+        default:
+            context->handleError(Error(GL_INVALID_ENUM, "Invalid stencil function."));
+            return false;
+    }
+
+    return true;
+}
+
+// Note that the spec specifies that for the path drawing commands
+// if the path object is not an existing path object the command
+// does nothing and no error is generated.
+// However if the path object exists but has not been specified any
+// commands then an error is generated.
+
+bool ValidateStencilFillPath(Context *context, GLuint path, GLenum fillMode, GLuint mask)
+{
+    if (!context->getExtensions().pathRendering)
+    {
+        context->handleError(
+            Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
+        return false;
+    }
+    if (context->hasPath(path) && !context->hasPathData(path))
+    {
+        context->handleError(Error(GL_INVALID_OPERATION, "No such path object."));
+        return false;
+    }
+
+    switch (fillMode)
+    {
+        case GL_COUNT_UP_CHROMIUM:
+        case GL_COUNT_DOWN_CHROMIUM:
+            break;
+        default:
+            context->handleError(Error(GL_INVALID_ENUM, "Invalid fill mode."));
+            return false;
+    }
+
+    if (!isPow2(mask + 1))
+    {
+        context->handleError(Error(GL_INVALID_VALUE, "Invalid stencil bit mask."));
+        return false;
+    }
+
+    return true;
+}
+
+bool ValidateStencilStrokePath(Context *context, GLuint path, GLint reference, GLuint mask)
+{
+    if (!context->getExtensions().pathRendering)
+    {
+        context->handleError(
+            Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
+        return false;
+    }
+    if (context->hasPath(path) && !context->hasPathData(path))
+    {
+        context->handleError(Error(GL_INVALID_OPERATION, "No such path or path has no data."));
+        return false;
+    }
+
+    return true;
+}
+
+bool ValidateCoverPath(Context *context, GLuint path, GLenum coverMode)
+{
+    if (!context->getExtensions().pathRendering)
+    {
+        context->handleError(
+            Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
+        return false;
+    }
+    if (context->hasPath(path) && !context->hasPathData(path))
+    {
+        context->handleError(Error(GL_INVALID_OPERATION, "No such path object."));
+        return false;
+    }
+
+    switch (coverMode)
+    {
+        case GL_CONVEX_HULL_CHROMIUM:
+        case GL_BOUNDING_BOX_CHROMIUM:
+            break;
+        default:
+            context->handleError(Error(GL_INVALID_ENUM, "Invalid cover mode."));
+            return false;
+    }
+    return true;
+}
+
+bool ValidateStencilThenCoverFillPath(Context *context,
+                                      GLuint path,
+                                      GLenum fillMode,
+                                      GLuint mask,
+                                      GLenum coverMode)
+{
+    return ValidateStencilFillPath(context, path, fillMode, mask) &&
+           ValidateCoverPath(context, path, coverMode);
+}
+
+bool ValidateStencilThenCoverStrokePath(Context *context,
+                                        GLuint path,
+                                        GLint reference,
+                                        GLuint mask,
+                                        GLenum coverMode)
+{
+    return ValidateStencilStrokePath(context, path, reference, mask) &&
+           ValidateCoverPath(context, path, coverMode);
+}
+
+bool ValidateIsPath(Context *context)
+{
+    if (!context->getExtensions().pathRendering)
+    {
+        context->handleError(
+            Error(GL_INVALID_OPERATION, "GL_CHROMIUM_path_rendering is not available."));
+        return false;
+    }
+    return true;
+}
+
 }  // namespace gl