public static boolean validateHTConstraints()

in src/main/java/com/netflix/imflibrary/st2067_2/Application2E2021.java [277:540]


    public static boolean validateHTConstraints(J2KHeaderParameters p,
                                     IMFErrorLogger logger) {
        boolean isValid = true;

        if (p == null) {
            logger.addError(
                    IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR,
                    IMFErrorLogger.IMFErrors.ErrorLevels.FATAL,
                    "APP2.HT: Missing or incomplete JPEG 2000 Sub-descriptor");
            return false;
        }

        if (p.xosiz != 0 || p.yosiz != 0 || p.xtosiz != 0 || p.ytosiz != 0) {
            logger.addError(
                    IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR,
                    IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL,
                    "APP2.HT: Invalid XOsiz, YOsiz, XTOsiz or YTOsiz");
            isValid = false;
        }

        if (p.xtsiz < p.xsiz || p.ytsiz < p.ysiz) {
            logger.addError(
                    IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR,
                    IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL,
                    "APP2.HT: Invalid XTsiz or XYsiz");
            isValid = false;
        }

        /* components constraints */

        if (p.csiz.length <= 0 || p.csiz.length > 4) {
            logger.addError(
                    IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR,
                    IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL,
                    String.format("APP2.HT: Invalid number (%d) of components", p.csiz.length));
            isValid = false;
        }

        /* x sub-sampling */
        if (p.csiz[0].xrsiz != 1) {
            logger.addError(
                    IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR,
                    IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL,
                    "APP2.HT: invalid horizontal sub-sampling for component 1");
            isValid = false;
        }
        if (p.csiz.length > 1 && p.csiz[1].xrsiz != 1 &&
            (p.csiz.length <= 2 || p.csiz[1].xrsiz != 2 || p.csiz[2].xrsiz != 2)) {
            logger.addError(
                    IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR,
                    IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL,
                    "APP2.HT: invalid horizontal sub-sampling for component 2");
            isValid = false;
        }
        if (p.csiz.length > 2 && p.csiz[2].xrsiz != 1 && (p.csiz[1].xrsiz != 2 || p.csiz[2].xrsiz != 2)) {
            logger.addError(
                    IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR,
                    IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL,
                    "APP2.HT: invalid horizontal sub-sampling for component 3");
            isValid = false;
        }
        if (p.csiz.length > 3 && p.csiz[3].xrsiz != 1) {
            logger.addError(
                    IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR,
                    IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL,
                    "APP2.HT: invalid horizontal sub-sampling for component 4");
            isValid = false;
        }

        /* y sub-sampling and sample width */
        if (p.csiz[0].ssiz > 15 || p.csiz[0].ssiz < 7) {
            logger.addError(
                    IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR,
                    IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL,
                    String.format("APP2.HT: Invalid bit depth (%d)", p.csiz[0].ssiz + 1));
            isValid = false;
        }
        for (int i = 0; i < p.csiz.length; i++) {
            if (p.csiz[i].yrsiz != 1) {
                logger.addError(
                        IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR,
                        IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL,
                        String.format("APP2.HT: invalid vertical sub-sampling for component %d", i));
                isValid = false;
            }
            if (p.csiz[i].ssiz != p.csiz[0].ssiz) {
                logger.addError(
                        IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR,
                        IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL,
                        "APP2.HT: all components must have the same bit depth");
                isValid = false;
            }
        }
        /* CAP constraints */

        /* Pcapi is 1 for i = 15, and 0 otherwise, per ST 2067-21 Annex I; therefore, pcap = 2^(32-15) = 131072 */
        if (p.cap == null || p.cap.pcap != 131072 || p.cap.ccap == null || p.cap.ccap.length != 1) {
            /* codestream shall require only Part 15 capabilities */
            logger.addError(
                    IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR,
                    IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL,
                    "APP2.HT: missing or invalid CAP marker");
            return false;
        }

        if ((p.cap.ccap[0] & 0b1111000000000000) != 0) {
            /* Bits 12-15 of Ccap15 shall be 0 */
            logger.addError(
                    IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR,
                    IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL,
                    "APP2.HT: Bits 12-15 of Ccap15 shall be 0");
            isValid = false;
        }

        boolean isHTREV = (p.cap.ccap[0] & 0b100000) == 0;

        /* COD */

        if (p.cod == null) {
            logger.addError(
                    IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR,
                    IMFErrorLogger.IMFErrors.ErrorLevels.FATAL,
                    "APP2.HT: Missing COD marker");
            return false;
        }

        /* no scod constraints */

        /* code-block style */
        if (p.cod.cbStyle != 0b01000000) {
            /* bad code-block style */
            logger.addError(
                    IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR,
                    IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL,
                    "APP2.HT: Invalid default code-block style");
            isValid = false;
        }

        /* progression order - RPCL is not required, but ST 2067-21:2023 Annex I Note 3 implies a preference */
        if (p.cod.progressionOrder != ProgressionOrder.RPCL.value())
            logger.addError(
                IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR,
                IMFErrorLogger.IMFErrors.ErrorLevels.WARNING,
                "APP2.HT: JPEG 2000 progression order is not RPCL");

        /* resolution layers */
        if (p.cod.numDecompLevels == 0) {
            logger.addError(
                IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR,
                IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL,
                "APP2.HT: Number of decomposition levels must be greater than 0");
            isValid = false;
        }


        long maxSz = Math.max(p.xsiz, p.ysiz);
        if ((maxSz <= 2048 && p.cod.numDecompLevels > 5) ||
            (maxSz <= 4096 && p.cod.numDecompLevels > 6) ||
            (maxSz <= 8192 && p.cod.numDecompLevels > 7)) {
            logger.addError(
                IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR,
                IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL,
                "APP2.HT: Invalid number of decomposition levels");
            isValid = false;
        }

        /* number of layers */

        if (p.cod.numLayers != 1) {
            logger.addError(
                IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR,
                IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL,
                String.format("APP2.HT: Number of layers (%d) is not 1", p.cod.numLayers));
            isValid = false;
        }

        /* code-block sizes */

        if (p.cod.ycb < 5 || p.cod.ycb > 6) {
            logger.addError(
                IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR,
                IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL,
                String.format("APP2.HT: Invalid vertical code-block size (ycb = %d)", p.cod.ycb));
            isValid = false;
        }

        if (p.cod.xcb < 5 || p.cod.xcb > 7) {
            logger.addError(
                IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR,
                IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL,
                String.format("APP2.HT: Invalid horizontal code-block size (xcb = %d)", p.cod.xcb));
            isValid = false;
        }


        /* transformation */

        boolean isReversibleFilter = (p.cod.transformation == 1);

        if (isHTREV && !isReversibleFilter) {
            logger.addError(
                IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR,
                IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL,
                "APP2.HT: 9-7 irreversible filter is used but HTREV is signaled in CAP");
            isValid = false;
        }

        /* precinct size */

        if (p.cod.precinctSizes.length == 0 || p.cod.precinctSizes[0] != 0x77) {
            logger.addError(
                IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR,
                IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL,
                "APP2.HT: Invalid precinct sizes");
            isValid = false;
        }

        for (int i = 1; i < p.cod.precinctSizes.length; i++)
            if (p.cod.precinctSizes[i] != 0x88) {
                logger.addError(
                    IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR,
                    IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL,
                    "APP2.HT: Invalid precinct sizes");
                isValid = false;
                break;
            }

        /* magbp - calculation according to ITU-T T.814 */


        int maxB = p.csiz[0].ssiz + 2;
        if (isReversibleFilter) {
            maxB += 2 + p.cod.multiComponentTransform;
            if (p.cod.numDecompLevels > 5)
                maxB += 1;
        } else if (p.cod.multiComponentTransform == 1 && p.csiz[0].ssiz > 9) {
            maxB += 1;
        }

        int codestreamB = (p.cap.ccap[0] & 0b11111) + 8;

        /*
        *   NOTE: The Parameter B constraints in ST 2067-21:2023 are arguably too narrow, and existing implementations do violate them under certain circumstances.
        *   Since practical issues are not expected from software decoders otherwise, an ERROR is currently returned only for values that exceed the max value (21)
        *   allowed for any configuration by ST 2067-21:2023. A WARNING is provided for values that exceed the limit stated in ST 2067-21:2023, but not 21.
        *
        *   TODO: This should be revisited as more implementations become available. Discussion for reference: https://github.com/SMPTE/st2067-21/issues/7
        */

        if (codestreamB > 21) {
            logger.addError(
                    IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR,
                    IMFErrorLogger.IMFErrors.ErrorLevels.NON_FATAL,
                    "APP2.HT: Parameter B has exceeded its limit to an extent that decoder issues are to be expected");
            isValid = false;
        } else if (codestreamB > maxB) {
            logger.addError(
                IMFErrorLogger.IMFErrors.ErrorCodes.APPLICATION_COMPOSITION_ERROR,
                IMFErrorLogger.IMFErrors.ErrorLevels.WARNING,
                "APP2.HT: Parameter B has exceeded its limits");
        }

        return isValid;
    }