"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _resource = _interopRequireDefault(require("./resource"));

var _texture2d = _interopRequireDefault(require("./texture-2d"));

var _renderbuffer = _interopRequireDefault(require("./renderbuffer"));

var _buffer = _interopRequireDefault(require("./buffer"));

var _clear2 = require("./clear");

var _webglContext = require("../webgl-context");

var _contextFeatures = require("../webgl-context/context-features");

var _typedArrayUtils = require("../webgl-utils/typed-array-utils");

var _formatUtils = require("../webgl-utils/format-utils");

var _webglUtils = require("../webgl-utils");

var _utils = require("../utils");

var _assert = _interopRequireDefault(require("../utils/assert"));

var _constants = _interopRequireDefault(require("../constants"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); }

function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); }

function _iterableToArrayLimit(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }

function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }

function _instanceof(left, right) { if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) { return right[Symbol.hasInstance](left); } else { return left instanceof right; } }

function _classCallCheck(instance, Constructor) { if (!_instanceof(instance, Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }

function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }

function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }

var ERR_MULTIPLE_RENDERTARGETS = 'Multiple render targets not supported';

var Framebuffer =
/*#__PURE__*/
function (_Resource) {
  _inherits(Framebuffer, _Resource);

  _createClass(Framebuffer, [{
    key: "MAX_COLOR_ATTACHMENTS",
    get: function get() {
      return this.gl.getParameter(this.gl.MAX_COLOR_ATTACHMENTS);
    }
  }, {
    key: "MAX_DRAW_BUFFERS",
    get: function get() {
      return this.gl.getParameter(this.gl.MAX_DRAW_BUFFERS);
    }
  }], [{
    key: "isSupported",
    value: function isSupported(gl) {
      var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
          colorBufferFloat = _ref.colorBufferFloat,
          colorBufferHalfFloat = _ref.colorBufferHalfFloat;

      var supported = true;
      supported = colorBufferFloat && gl.getExtension((0, _webglUtils.isWebGL2)(gl) ? 'EXT_color_buffer_float' : 'WEBGL.color_buffer_float');
      supported = colorBufferHalfFloat && gl.getExtension((0, _webglUtils.isWebGL2)(gl) ? 'EXT_color_buffer_float' : 'EXT_color_buffer_half_float');
      return supported;
    } // Create a Framebuffer wrapper for the default framebuffer (target === null)

  }, {
    key: "getDefaultFramebuffer",
    value: function getDefaultFramebuffer(gl) {
      gl.luma = gl.luma || {};

      if (!gl.luma.defaultFramebuffer) {
        gl.luma.defaultFramebuffer = new Framebuffer(gl, {
          handle: null,
          attachments: {}
        });
      } // TODO - can we query for and get a handle to the GL.FRONT renderbuffer?


      return gl.luma.defaultFramebuffer;
    }
  }]);

  function Framebuffer(gl) {
    var _this;

    var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};

    _classCallCheck(this, Framebuffer);

    _this = _possibleConstructorReturn(this, (Framebuffer.__proto__ || Object.getPrototypeOf(Framebuffer)).call(this, gl, opts)); // Public members

    _this.width = null;
    _this.height = null;
    _this.attachments = {};
    _this.readBuffer = _constants.default.COLOR_ATTACHMENT0;
    _this.drawBuffers = [_constants.default.COLOR_ATTACHMENT0];

    _this.initialize(opts);

    Object.seal(_assertThisInitialized(_this));
    return _this;
  }

  _createClass(Framebuffer, [{
    key: "initialize",
    value: function initialize(_ref2) {
      var _ref2$width = _ref2.width,
          width = _ref2$width === void 0 ? 1 : _ref2$width,
          _ref2$height = _ref2.height,
          height = _ref2$height === void 0 ? 1 : _ref2$height,
          _ref2$attachments = _ref2.attachments,
          attachments = _ref2$attachments === void 0 ? null : _ref2$attachments,
          _ref2$color = _ref2.color,
          color = _ref2$color === void 0 ? true : _ref2$color,
          _ref2$depth = _ref2.depth,
          depth = _ref2$depth === void 0 ? true : _ref2$depth,
          _ref2$stencil = _ref2.stencil,
          stencil = _ref2$stencil === void 0 ? false : _ref2$stencil,
          _ref2$check = _ref2.check,
          check = _ref2$check === void 0 ? true : _ref2$check,
          readBuffer = _ref2.readBuffer,
          drawBuffers = _ref2.drawBuffers;
      (0, _assert.default)(width >= 0 && height >= 0, 'Width and height need to be integers'); // Store actual width and height for diffing

      this.width = width;
      this.height = height; // Resize any provided attachments - note that resize only resizes if needed
      // Note: A framebuffer has no separate size, it is defined by its attachments (which must agree)

      if (attachments) {
        for (var attachment in attachments) {
          var target = attachments[attachment];
          var object = Array.isArray(target) ? target[0] : target;
          object.resize({
            width: width,
            height: height
          });
        }
      } else {
        // Create any requested default attachments
        attachments = this._createDefaultAttachments({
          color: color,
          depth: depth,
          stencil: stencil,
          width: width,
          height: height
        });
      }

      this.update({
        clearAttachments: true,
        attachments: attachments,
        readBuffer: readBuffer,
        drawBuffers: drawBuffers
      }); // Checks that framebuffer was properly set up, if not, throws an explanatory error

      if (attachments && check) {
        this.checkStatus();
      }
    }
  }, {
    key: "update",
    value: function update(_ref3) {
      var _ref3$attachments = _ref3.attachments,
          attachments = _ref3$attachments === void 0 ? {} : _ref3$attachments,
          readBuffer = _ref3.readBuffer,
          drawBuffers = _ref3.drawBuffers,
          _ref3$clearAttachment = _ref3.clearAttachments,
          clearAttachments = _ref3$clearAttachment === void 0 ? false : _ref3$clearAttachment;
      this.attach(attachments, {
        clearAttachments: clearAttachments
      });
      var gl = this.gl; // Multiple render target support, set read buffer and draw buffers

      var prevHandle = gl.bindFramebuffer(_constants.default.FRAMEBUFFER, this.handle);

      if (readBuffer) {
        this._setReadBuffer(readBuffer);
      }

      if (drawBuffers) {
        this._setDrawBuffers(drawBuffers);
      }

      gl.bindFramebuffer(_constants.default.FRAMEBUFFER, prevHandle || null);
      return this;
    } // Attachment resize is expected to be a noop if size is same

  }, {
    key: "resize",
    value: function resize() {
      var _ref4 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
          width = _ref4.width,
          height = _ref4.height;

      // for default framebuffer, just update the stored size
      if (this.handle === null) {
        (0, _assert.default)(width === undefined && height === undefined);
        this.width = this.gl.drawingBufferWidth;
        this.height = this.gl.drawingBufferHeight;
        return this;
      }

      if (width === undefined) {
        width = this.gl.drawingBufferWidth;
      }

      if (height === undefined) {
        height = this.gl.drawingBufferHeight;
      }

      if (width !== this.width && height !== this.height) {
        _utils.log.log(2, "Resizing framebuffer ".concat(this.id, " to ").concat(width, "x").concat(height));
      }

      for (var attachmentPoint in this.attachments) {
        this.attachments[attachmentPoint].resize({
          width: width,
          height: height
        });
      }

      this.width = width;
      this.height = height;
      return this;
    } // Attach from a map of attachments

  }, {
    key: "attach",
    value: function attach(attachments) {
      var _this2 = this;

      var _ref5 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
          _ref5$clearAttachment = _ref5.clearAttachments,
          clearAttachments = _ref5$clearAttachment === void 0 ? false : _ref5$clearAttachment;

      var newAttachments = {}; // Any current attachments need to be removed, add null values to map

      if (clearAttachments) {
        Object.keys(this.attachments).forEach(function (key) {
          newAttachments[key] = null;
        });
      } // Overlay the new attachments


      Object.assign(newAttachments, attachments);
      var prevHandle = this.gl.bindFramebuffer(_constants.default.FRAMEBUFFER, this.handle); // Walk the attachments

      for (var attachment in newAttachments) {
        // Ensure key is not undefined
        (0, _assert.default)(attachment !== 'undefined', 'Misspelled framebuffer binding point?');
        var descriptor = newAttachments[attachment];
        var object = descriptor;

        if (!object) {
          this._unattach({
            attachment: attachment
          });
        } else if (_instanceof(object, _renderbuffer.default)) {
          this._attachRenderbuffer({
            attachment: attachment,
            renderbuffer: object
          });
        } else if (Array.isArray(descriptor)) {
          var _descriptor = _slicedToArray(descriptor, 3),
              texture = _descriptor[0],
              _descriptor$ = _descriptor[1],
              layer = _descriptor$ === void 0 ? 0 : _descriptor$,
              _descriptor$2 = _descriptor[2],
              level = _descriptor$2 === void 0 ? 0 : _descriptor$2;

          object = texture;

          this._attachTexture({
            attachment: attachment,
            texture: texture,
            layer: layer,
            level: level
          });
        } else {
          this._attachTexture({
            attachment: attachment,
            texture: object,
            layer: 0,
            level: 0
          });
        } // Resize objects


        if (object) {
          object.resize({
            width: this.width,
            height: this.height
          });
        }
      }

      this.gl.bindFramebuffer(_constants.default.FRAMEBUFFER, prevHandle || null); // Assign to attachments and remove any nulls to get a clean attachment map

      Object.assign(this.attachments, attachments);
      Object.keys(this.attachments).filter(function (key) {
        return !_this2.attachments[key];
      }).forEach(function (key) {
        delete _this2.attachments[key];
      });
    }
  }, {
    key: "checkStatus",
    value: function checkStatus() {
      var gl = this.gl;
      var prevHandle = gl.bindFramebuffer(_constants.default.FRAMEBUFFER, this.handle);
      var status = gl.checkFramebufferStatus(_constants.default.FRAMEBUFFER);
      gl.bindFramebuffer(_constants.default.FRAMEBUFFER, prevHandle || null);

      if (status !== gl.FRAMEBUFFER_COMPLETE) {
        throw new Error(_getFrameBufferStatus(status));
      }

      return this;
    }
  }, {
    key: "clear",
    value: function clear() {
      var _ref6 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
          color = _ref6.color,
          depth = _ref6.depth,
          stencil = _ref6.stencil,
          _ref6$drawBuffers = _ref6.drawBuffers,
          drawBuffers = _ref6$drawBuffers === void 0 ? [] : _ref6$drawBuffers;

      // Bind framebuffer and delegate to global clear functions
      var prevHandle = this.gl.bindFramebuffer(_constants.default.FRAMEBUFFER, this.handle);

      if (color || depth || stencil) {
        (0, _clear2.clear)(this.gl, {
          color: color,
          depth: depth,
          stencil: stencil
        });
      }

      drawBuffers.forEach(function (value, drawBuffer) {
        (0, _clear2.clearBuffer)({
          drawBuffer: drawBuffer,
          value: value
        });
      });
      this.gl.bindFramebuffer(_constants.default.FRAMEBUFFER, prevHandle || null);
      return this;
    } // NOTE: Slow requires roundtrip to GPU
    // App can provide pixelArray or have it auto allocated by this method
    // @returns {Uint8Array|Uint16Array|FloatArray} - pixel array,
    //  newly allocated by this method unless provided by app.

  }, {
    key: "readPixels",
    value: function readPixels(_ref7) {
      var _ref7$x = _ref7.x,
          x = _ref7$x === void 0 ? 0 : _ref7$x,
          _ref7$y = _ref7.y,
          y = _ref7$y === void 0 ? 0 : _ref7$y,
          _ref7$width = _ref7.width,
          width = _ref7$width === void 0 ? this.width : _ref7$width,
          _ref7$height = _ref7.height,
          height = _ref7$height === void 0 ? this.height : _ref7$height,
          _ref7$format = _ref7.format,
          format = _ref7$format === void 0 ? _constants.default.RGBA : _ref7$format,
          type = _ref7.type,
          _ref7$pixelArray = _ref7.pixelArray,
          pixelArray = _ref7$pixelArray === void 0 ? null : _ref7$pixelArray,
          _ref7$attachment = _ref7.attachment,
          attachment = _ref7$attachment === void 0 ? _constants.default.COLOR_ATTACHMENT0 : _ref7$attachment;
      var gl = this.gl; // TODO - Set and unset gl.readBuffer

      if (attachment === _constants.default.COLOR_ATTACHMENT0 && this.handle === null) {
        attachment = _constants.default.FRONT;
      } // Deduce type and allocated pixelArray if needed


      if (!pixelArray) {
        // Allocate pixel array if not already available, using supplied type
        type = type || gl.UNSIGNED_BYTE;
        var ArrayType = (0, _typedArrayUtils.getTypedArrayFromGLType)(type, {
          clamped: false
        });
        var components = (0, _formatUtils.glFormatToComponents)(format); // TODO - check for composite type (components = 1).

        pixelArray = pixelArray || new ArrayType(width * height * components);
      } // Pixel array available, if necessary, deduce type from it.


      type = type || (0, _typedArrayUtils.getGLTypeFromTypedArray)(pixelArray);
      var prevHandle = this.gl.bindFramebuffer(_constants.default.FRAMEBUFFER, this.handle);
      this.gl.readPixels(x, y, width, height, format, type, pixelArray);
      this.gl.bindFramebuffer(_constants.default.FRAMEBUFFER, prevHandle || null);
      return pixelArray;
    } // Reads data into provided buffer object asynchronously
    // This function doesn't wait for copy to be complete, it programs GPU to perform a DMA transffer.

  }, {
    key: "readPixelsToBuffer",
    value: function readPixelsToBuffer(_ref8) {
      var _ref8$x = _ref8.x,
          x = _ref8$x === void 0 ? 0 : _ref8$x,
          _ref8$y = _ref8.y,
          y = _ref8$y === void 0 ? 0 : _ref8$y,
          _ref8$width = _ref8.width,
          width = _ref8$width === void 0 ? this.width : _ref8$width,
          _ref8$height = _ref8.height,
          height = _ref8$height === void 0 ? this.height : _ref8$height,
          _ref8$format = _ref8.format,
          format = _ref8$format === void 0 ? _constants.default.RGBA : _ref8$format,
          type = _ref8.type,
          _ref8$buffer = _ref8.buffer,
          buffer = _ref8$buffer === void 0 ? null : _ref8$buffer,
          _ref8$byteOffset = _ref8.byteOffset,
          byteOffset = _ref8$byteOffset === void 0 ? 0 : _ref8$byteOffset;
      var gl = this.gl; // Asynchronus read (PIXEL_PACK_BUFFER) is WebGL2 only feature

      (0, _webglUtils.assertWebGL2Context)(gl); // deduce type if not available.

      type = type || (buffer ? buffer.type : _constants.default.UNSIGNED_BYTE);

      if (!buffer) {
        // Create new buffer with enough size
        var components = (0, _formatUtils.glFormatToComponents)(format);
        var byteCount = (0, _formatUtils.glTypeToBytes)(type);
        var bytes = byteOffset + width * height * components * byteCount;
        buffer = new _buffer.default(gl, {
          bytes: bytes,
          type: type,
          size: components
        });
      }

      buffer.bind({
        target: _constants.default.PIXEL_PACK_BUFFER
      });
      (0, _webglContext.withParameters)(gl, {
        framebuffer: this
      }, function () {
        gl.readPixels(x, y, width, height, format, type, byteOffset);
      });
      buffer.unbind({
        target: _constants.default.PIXEL_PACK_BUFFER
      });
      return buffer;
    } // Reads pixels as a dataUrl

  }, {
    key: "copyToDataUrl",
    value: function copyToDataUrl() {
      var _ref9 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
          _ref9$attachment = _ref9.attachment,
          attachment = _ref9$attachment === void 0 ? _constants.default.COLOR_ATTACHMENT0 : _ref9$attachment,
          _ref9$maxHeight = _ref9.maxHeight,
          maxHeight = _ref9$maxHeight === void 0 ? Number.MAX_SAFE_INTEGER : _ref9$maxHeight;

      var data = this.readPixels({
        attachment: attachment
      }); // Scale down

      var width = this.width,
          height = this.height;

      while (height > maxHeight) {
        var _scalePixels = (0, _webglUtils.scalePixels)({
          data: data,
          width: width,
          height: height
        });

        data = _scalePixels.data;
        width = _scalePixels.width;
        height = _scalePixels.height;
      } // Flip to top down coordinate system


      (0, _webglUtils.flipRows)({
        data: data,
        width: width,
        height: height
      });
      /* global document */

      var canvas = document.createElement('canvas');
      canvas.width = width;
      canvas.height = height;
      var context = canvas.getContext('2d'); // Copy the pixels to a 2D canvas

      var imageData = context.createImageData(width, height);
      imageData.data.set(data);
      context.putImageData(imageData, 0, 0);
      return canvas.toDataURL();
    } // Reads pixels into an HTML Image

  }, {
    key: "copyToImage",
    value: function copyToImage() {
      var _ref10 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
          _ref10$image = _ref10.image,
          image = _ref10$image === void 0 ? null : _ref10$image,
          _ref10$attachment = _ref10.attachment,
          attachment = _ref10$attachment === void 0 ? _constants.default.COLOR_ATTACHMENT0 : _ref10$attachment,
          _ref10$maxHeight = _ref10.maxHeight,
          maxHeight = _ref10$maxHeight === void 0 ? Number.MAX_SAFE_INTEGER : _ref10$maxHeight;

      /* global Image */
      var dataUrl = this.readDataUrl({
        attachment: attachment
      });
      image = image || new Image();
      image.src = dataUrl;
      return image;
    } // copyToFramebuffer({width, height}) {
    //   const scaleX = width / this.width;
    //   const scaleY = height / this.height;
    //   const scale = Math.min(scaleX, scaleY);
    //   width = width * scale;
    //   height = height * scale;
    //   const scaledFramebuffer = new Framebuffer(this.gl, {width, height});
    //   this.blit();
    // }
    // Copy a rectangle from a framebuffer attachment into a texture (at an offset)

  }, {
    key: "copyToTexture",
    value: function copyToTexture(_ref11) {
      var texture = _ref11.texture,
          target = _ref11.target,
          _ref11$xoffset = _ref11.xoffset,
          xoffset = _ref11$xoffset === void 0 ? 0 : _ref11$xoffset,
          _ref11$yoffset = _ref11.yoffset,
          yoffset = _ref11$yoffset === void 0 ? 0 : _ref11$yoffset,
          _ref11$zoffset = _ref11.zoffset,
          zoffset = _ref11$zoffset === void 0 ? 0 : _ref11$zoffset,
          _ref11$mipmapLevel = _ref11.mipmapLevel,
          mipmapLevel = _ref11$mipmapLevel === void 0 ? 0 : _ref11$mipmapLevel,
          _ref11$attachment = _ref11.attachment,
          attachment = _ref11$attachment === void 0 ? _constants.default.COLOR_ATTACHMENT0 : _ref11$attachment,
          _ref11$x = _ref11.x,
          x = _ref11$x === void 0 ? 0 : _ref11$x,
          _ref11$y = _ref11.y,
          y = _ref11$y === void 0 ? 0 : _ref11$y,
          width = _ref11.width,
          height = _ref11.height;
      var gl = this.gl;
      var prevHandle = gl.bindFramebuffer(_constants.default.FRAMEBUFFER, this.handle);
      var prevBuffer = gl.readBuffer(attachment);
      width = Number.isFinite(width) ? width : texture.width;
      height = Number.isFinite(height) ? height : texture.height; // target

      switch (texture.target) {
        case _constants.default.TEXTURE_2D:
        case _constants.default.TEXTURE_CUBE_MAP:
          gl.copyTexSubImage2D(target || texture.target, mipmapLevel, xoffset, yoffset, x, y, width, height);
          break;

        case _constants.default.TEXTURE_2D_ARRAY:
        case _constants.default.TEXTURE_3D:
          gl.copyTexSubImage3D(target || texture.target, mipmapLevel, xoffset, yoffset, zoffset, x, y, width, height);
          break;

        default:
      }

      gl.readBuffer(prevBuffer);
      gl.bindFramebuffer(_constants.default.FRAMEBUFFER, prevHandle || null);
      return texture;
    } // WEBGL2 INTERFACE
    // Copies a rectangle of pixels between framebuffers

  }, {
    key: "blit",
    value: function blit(_ref12) {
      var srcFramebuffer = _ref12.srcFramebuffer,
          _ref12$attachment = _ref12.attachment,
          attachment = _ref12$attachment === void 0 ? _constants.default.COLOR_ATTACHMENT0 : _ref12$attachment,
          _ref12$srcX = _ref12.srcX0,
          srcX0 = _ref12$srcX === void 0 ? 0 : _ref12$srcX,
          _ref12$srcY = _ref12.srcY0,
          srcY0 = _ref12$srcY === void 0 ? 0 : _ref12$srcY,
          srcX1 = _ref12.srcX1,
          srcY1 = _ref12.srcY1,
          _ref12$dstX = _ref12.dstX0,
          dstX0 = _ref12$dstX === void 0 ? 0 : _ref12$dstX,
          _ref12$dstY = _ref12.dstY0,
          dstY0 = _ref12$dstY === void 0 ? 0 : _ref12$dstY,
          dstX1 = _ref12.dstX1,
          dstY1 = _ref12.dstY1,
          _ref12$color = _ref12.color,
          color = _ref12$color === void 0 ? true : _ref12$color,
          _ref12$depth = _ref12.depth,
          depth = _ref12$depth === void 0 ? false : _ref12$depth,
          _ref12$stencil = _ref12.stencil,
          stencil = _ref12$stencil === void 0 ? false : _ref12$stencil,
          _ref12$mask = _ref12.mask,
          mask = _ref12$mask === void 0 ? 0 : _ref12$mask,
          _ref12$filter = _ref12.filter,
          filter = _ref12$filter === void 0 ? _constants.default.NEAREST : _ref12$filter;
      var gl = this.gl;
      (0, _webglUtils.assertWebGL2Context)(gl);

      if (!srcFramebuffer.handle && attachment === _constants.default.COLOR_ATTACHMENT0) {
        attachment = _constants.default.FRONT;
      }

      if (color) {
        mask |= _constants.default.COLOR_BUFFER_BIT;
      }

      if (depth) {
        mask |= _constants.default.DEPTH_BUFFER_BIT;
      }

      if (stencil) {
        mask |= _constants.default.STENCIL_BUFFER_BIT;
      }

      (0, _assert.default)(mask);
      srcX1 = srcX1 === undefined ? srcFramebuffer.width : srcX1;
      srcY1 = srcY1 === undefined ? srcFramebuffer.height : srcY1;
      dstX1 = dstX1 === undefined ? this.width : dstX1;
      dstY1 = dstY1 === undefined ? this.height : dstY1;
      var prevDrawHandle = gl.bindFramebuffer(_constants.default.DRAW_FRAMEBUFFER, this.handle);
      var prevReadHandle = gl.bindFramebuffer(_constants.default.READ_FRAMEBUFFER, srcFramebuffer.handle);
      gl.readBuffer(attachment);
      gl.blitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
      gl.readBuffer(this.readBuffer);
      gl.bindFramebuffer(_constants.default.READ_FRAMEBUFFER, prevReadHandle || null);
      gl.bindFramebuffer(_constants.default.DRAW_FRAMEBUFFER, prevDrawHandle || null);
      return this;
    } // signals to the GL that it need not preserve all pixels of a specified region of the framebuffer

  }, {
    key: "invalidate",
    value: function invalidate(_ref13) {
      var _ref13$attachments = _ref13.attachments,
          attachments = _ref13$attachments === void 0 ? [] : _ref13$attachments,
          _ref13$x = _ref13.x,
          x = _ref13$x === void 0 ? 0 : _ref13$x,
          _ref13$y = _ref13.y,
          y = _ref13$y === void 0 ? 0 : _ref13$y,
          width = _ref13.width,
          height = _ref13.height;
      var gl = this.gl;
      (0, _webglUtils.assertWebGL2Context)(gl);
      var prevHandle = gl.bindFramebuffer(_constants.default.READ_FRAMEBUFFER, this.handle);
      var invalidateAll = x === 0 && y === 0 && width === undefined && height === undefined;

      if (invalidateAll) {
        gl.invalidateFramebuffer(_constants.default.READ_FRAMEBUFFER, attachments);
      } else {
        gl.invalidateFramebuffer(_constants.default.READ_FRAMEBUFFER, attachments, x, y, width, height);
      }

      gl.bindFramebuffer(_constants.default.READ_FRAMEBUFFER, prevHandle);
      return this;
    } // Return the value for `pname` of the specified attachment.
    // The type returned is the type of the requested pname

  }, {
    key: "getAttachmentParameter",
    value: function getAttachmentParameter() {
      var _ref14 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
          _ref14$attachment = _ref14.attachment,
          attachment = _ref14$attachment === void 0 ? _constants.default.COLOR_ATTACHMENT0 : _ref14$attachment,
          pname = _ref14.pname;

      var value = this._getAttachmentParameterFallback(pname);

      if (value === null) {
        this.gl.bindTexture(_constants.default.FRAMEBUFFER, this.handle);
        value = this.gl.getFramebufferAttachmentParameter(_constants.default.FRAMEBUFFER, attachment, pname);
        this.gl.bindTexture(_constants.default.FRAMEBUFFER, null);
      }

      return value;
    }
  }, {
    key: "getAttachmentParameters",
    value: function getAttachmentParameters() {
      var attachment = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _constants.default.COLOR_ATTACHMENT0;
      var parameters = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.constructor.ATTACHMENT_PARAMETERS || {};
      var values = {};

      for (var pname in parameters) {
        values[pname] = this.getAttachmentParameter(pname);
      }

      return this;
    } // DEBUG
    // Note: Will only work when called in an event handler

  }, {
    key: "show",
    value: function show() {
      /* global window */
      if (typeof window !== 'undefined') {
        window.open(this.copyToDataUrl(), 'luma-debug-texture');
      }

      return this;
    }
  }, {
    key: "log",
    value: function log() {
      var _ref15 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
          _ref15$priority = _ref15.priority,
          priority = _ref15$priority === void 0 ? 0 : _ref15$priority,
          _ref15$message = _ref15.message,
          message = _ref15$message === void 0 ? '' : _ref15$message;

      if (priority > _utils.log.priority || typeof window === 'undefined') {
        return this;
      }

      message = message || "Framebuffer ".concat(this.id);
      var image = this.copyToDataUrl({
        maxHeight: 100
      });

      _utils.log.image({
        priority: priority,
        message: message,
        image: image
      }, message)();

      return this;
    } // WEBGL INTERFACE

  }, {
    key: "bind",
    value: function bind() {
      var _ref16 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
          _ref16$target = _ref16.target,
          target = _ref16$target === void 0 ? _constants.default.FRAMEBUFFER : _ref16$target;

      this.gl.bindFramebuffer(target, this.handle);
      return this;
    }
  }, {
    key: "unbind",
    value: function unbind() {
      var _ref17 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
          _ref17$target = _ref17.target,
          target = _ref17$target === void 0 ? _constants.default.FRAMEBUFFER : _ref17$target;

      this.gl.bindFramebuffer(target, null);
      return this;
    } // PRIVATE METHODS

  }, {
    key: "_createDefaultAttachments",
    value: function _createDefaultAttachments(_ref18) {
      var color = _ref18.color,
          depth = _ref18.depth,
          stencil = _ref18.stencil,
          width = _ref18.width,
          height = _ref18.height;
      var defaultAttachments = null; // Add a color buffer if requested and not supplied

      if (color) {
        var _parameters;

        defaultAttachments = defaultAttachments || {};
        defaultAttachments[_constants.default.COLOR_ATTACHMENT0] = new _texture2d.default(this.gl, {
          pixels: null,
          // reserves texture memory, but texels are undefined
          format: _constants.default.RGBA,
          type: _constants.default.UNSIGNED_BYTE,
          width: width,
          height: height,
          // Note: Mipmapping can be disabled by texture resource when we resize the texture
          // to a non-power-of-two dimenstion (NPOT texture) under WebGL1. To have consistant
          // behavior we always disable mipmaps.
          mipmaps: false,
          // Set MIN and MAG filtering parameters so mipmaps are not used in sampling.
          // Set WRAP modes that support NPOT textures too.
          parameters: (_parameters = {}, _defineProperty(_parameters, _constants.default.TEXTURE_MIN_FILTER, _constants.default.NEAREST), _defineProperty(_parameters, _constants.default.TEXTURE_MAG_FILTER, _constants.default.NEAREST), _defineProperty(_parameters, _constants.default.TEXTURE_WRAP_S, _constants.default.CLAMP_TO_EDGE), _defineProperty(_parameters, _constants.default.TEXTURE_WRAP_T, _constants.default.CLAMP_TO_EDGE), _parameters)
        });
      } // Add a depth buffer if requested and not supplied


      if (depth) {
        defaultAttachments = defaultAttachments || {};
        defaultAttachments[_constants.default.DEPTH_ATTACHMENT] = new _renderbuffer.default(this.gl, {
          format: _constants.default.DEPTH_COMPONENT16,
          width: width,
          height: height
        });
      } // TODO - handle stencil and combined depth and stencil


      return defaultAttachments;
    }
  }, {
    key: "_unattach",
    value: function _unattach(_ref19) {
      var attachment = _ref19.attachment;
      this.gl.bindRenderbuffer(_constants.default.RENDERBUFFER, this.handle);
      this.gl.framebufferRenderbuffer(_constants.default.FRAMEBUFFER, attachment, _constants.default.RENDERBUFFER, null);
      delete this.attachments[attachment];
    }
  }, {
    key: "_attachRenderbuffer",
    value: function _attachRenderbuffer(_ref20) {
      var _ref20$attachment = _ref20.attachment,
          attachment = _ref20$attachment === void 0 ? _constants.default.COLOR_ATTACHMENT0 : _ref20$attachment,
          renderbuffer = _ref20.renderbuffer;
      var gl = this.gl; // TODO - is the bind needed?
      // gl.bindRenderbuffer(GL.RENDERBUFFER, renderbuffer.handle);

      gl.framebufferRenderbuffer(_constants.default.FRAMEBUFFER, attachment, _constants.default.RENDERBUFFER, renderbuffer.handle); // TODO - is the unbind needed?
      // gl.bindRenderbuffer(GL.RENDERBUFFER, null);

      this.attachments[attachment] = renderbuffer;
    } // layer = 0 - index into Texture2DArray and Texture3D or face for `TextureCubeMap`
    // level = 0 - mipmapLevel (must be 0 in WebGL1)

  }, {
    key: "_attachTexture",
    value: function _attachTexture(_ref21) {
      var _ref21$attachment = _ref21.attachment,
          attachment = _ref21$attachment === void 0 ? _constants.default.COLOR_ATTACHMENT0 : _ref21$attachment,
          texture = _ref21.texture,
          layer = _ref21.layer,
          level = _ref21.level;
      var gl = this.gl;
      gl.bindTexture(texture.target, texture.handle);

      switch (texture.target) {
        case _constants.default.TEXTURE_2D_ARRAY:
        case _constants.default.TEXTURE_3D:
          gl.framebufferTextureLayer(_constants.default.FRAMEBUFFER, attachment, texture.target, level, layer);
          break;

        case _constants.default.TEXTURE_CUBE_MAP:
          // layer must be a cubemap face (or if index, converted to cube map face)
          var face = mapIndexToCubeMapFace(layer);
          gl.framebufferTexture2D(_constants.default.FRAMEBUFFER, attachment, face, texture.handle, level);
          break;

        case _constants.default.TEXTURE_2D:
          gl.framebufferTexture2D(_constants.default.FRAMEBUFFER, attachment, _constants.default.TEXTURE_2D, texture.handle, level);
          break;

        default:
          (0, _assert.default)(false, 'Illegal texture type');
      }

      gl.bindTexture(texture.target, null);
      this.attachments[attachment] = texture;
    } // Expects framebuffer to be bound

  }, {
    key: "_setReadBuffer",
    value: function _setReadBuffer(gl, readBuffer) {
      if ((0, _webglUtils.isWebGL2)(gl)) {
        gl.readBuffer(readBuffer);
      } else {
        // Setting to color attachment 0 is a noop, so allow it in WebGL1
        (0, _assert.default)(readBuffer === _constants.default.COLOR_ATTACHMENT0 || readBuffer === _constants.default.BACK, ERR_MULTIPLE_RENDERTARGETS);
      }

      this.readBuffer = readBuffer;
    } // Expects framebuffer to be bound

  }, {
    key: "_setDrawBuffers",
    value: function _setDrawBuffers(gl, drawBuffers) {
      if ((0, _webglUtils.isWebGL2)(gl)) {
        gl.drawBuffers(drawBuffers);
      } else {
        var ext = gl.getExtension('WEBGL.draw_buffers');

        if (ext) {
          ext.drawBuffersWEBGL(drawBuffers);
        } else {
          // Setting a single draw buffer to color attachment 0 is a noop, allow in WebGL1
          (0, _assert.default)(drawBuffers.length === 1 && (drawBuffers[0] === _constants.default.COLOR_ATTACHMENT0 || drawBuffers[0] === _constants.default.BACK), ERR_MULTIPLE_RENDERTARGETS);
        }
      }

      this.drawBuffers = drawBuffers;
    } // Attempt to provide workable defaults for WebGL2 symbols under WebGL1
    // null means OK to query

    /* eslint-disable complexity */

  }, {
    key: "_getAttachmentParameterFallback",
    value: function _getAttachmentParameterFallback(pname) {
      var caps = (0, _contextFeatures.getFeatures)(this.gl);

      switch (pname) {
        case _constants.default.FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER:
          // GLint
          return !caps.webgl2 ? 0 : null;

        case _constants.default.FRAMEBUFFER_ATTACHMENT_RED_SIZE: // GLint

        case _constants.default.FRAMEBUFFER_ATTACHMENT_GREEN_SIZE: // GLint

        case _constants.default.FRAMEBUFFER_ATTACHMENT_BLUE_SIZE: // GLint

        case _constants.default.FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE: // GLint

        case _constants.default.FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE: // GLint

        case _constants.default.FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE:
          // GLint
          return !caps.webgl2 ? 8 : null;

        case _constants.default.FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE:
          // GLenum
          return !caps.webgl2 ? _constants.default.UNSIGNED_INT : null;

        case _constants.default.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING:
          return !caps.webgl2 && !caps.EXT_sRGB ? _constants.default.LINEAR : null;

        default:
          return null;
      }
    }
    /* eslint-enable complexity */
    // RESOURCE METHODS

  }, {
    key: "_createHandle",
    value: function _createHandle() {
      return this.gl.createFramebuffer();
    }
  }, {
    key: "_deleteHandle",
    value: function _deleteHandle() {
      this.gl.deleteFramebuffer(this.handle);
    }
  }, {
    key: "_bindHandle",
    value: function _bindHandle(handle) {
      return this.gl.bindFramebuffer(_constants.default.FRAMEBUFFER, handle);
    }
  }, {
    key: "color",
    get: function get() {
      return this.attachments[_constants.default.COLOR_ATTACHMENT0] || null;
    }
  }, {
    key: "texture",
    get: function get() {
      return this.attachments[_constants.default.COLOR_ATTACHMENT0] || null;
    }
  }, {
    key: "depth",
    get: function get() {
      return this.attachments[_constants.default.DEPTH_ATTACHMENT] || null;
    }
  }, {
    key: "stencil",
    get: function get() {
      return this.attachments[_constants.default.STENCIL_ATTACHMENT] || null;
    }
  }]);

  return Framebuffer;
}(_resource.default); // PUBLIC METHODS
// Map an index to a cube map face constant


exports.default = Framebuffer;

function mapIndexToCubeMapFace(layer) {
  // TEXTURE_CUBE_MAP_POSITIVE_X is a big value (0x8515)
  // if smaller assume layer is index, otherwise assume it is already a cube map face constant
  return layer < _constants.default.TEXTURE_CUBE_MAP_POSITIVE_X ? layer + _constants.default.TEXTURE_CUBE_MAP_POSITIVE_X : layer;
} // Helper METHODS
// Get a string describing the framebuffer error if installed


function _getFrameBufferStatus(status) {
  // Use error mapping if installed
  var STATUS = Framebuffer.STATUS || {};
  return STATUS[status] || "Framebuffer error ".concat(status);
}
//# sourceMappingURL=framebuffer.js.map