text: /*
   * Copyright 2021 The Chromium OS Authors. All rights reserved.
   * Use of this source code is governed by a BSD-style license that can be
   * found in the LICENSE file.
   */
  
  #include "gpu/egl/egl_image.h"
  
  #include <utility>
  #include <vector>
  
  #include <base/logging.h>
  #include <drm_fourcc.h>
  
  #include "cros-camera/camera_buffer_manager.h"
  #include "cros-camera/common.h"
  #include "gpu/egl/utils.h"
  
  namespace {
  
  PFNEGLCREATEIMAGEKHRPROC g_eglCreateImageKHR = nullptr;
  PFNEGLDESTROYIMAGEKHRPROC g_eglDestroyImageKHR = nullptr;
  
  }  // namespace
  
  namespace cros {
  
  namespace {
  
  bool IsPlanarYuv(uint32_t drm_format) {
    switch (drm_format) {
      case DRM_FORMAT_P010:
      case DRM_FORMAT_NV12:
      case DRM_FORMAT_NV21:
        return true;
      default:
        return false;
    }
  }
  
  }  // namespace
  
  // static
  bool EglImage::IsSupported() {
    static bool supported = []() -> bool {
      g_eglCreateImageKHR = reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>(
          eglGetProcAddress("eglCreateImageKHR"));
      g_eglDestroyImageKHR = reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC>(
          eglGetProcAddress("eglDestroyImageKHR"));
      return (g_eglCreateImageKHR != nullptr) &&
             (g_eglDestroyImageKHR != nullptr);
    }();
    return supported;
  }
  
  // static
  EglImage EglImage::FromBufferPlane(buffer_handle_t buffer,
                                     int plane,
                                     int width,
                                     int height,
                                     uint32_t drm_format) {
    std::vector<EGLint> attrs = {EGL_WIDTH,
                                 static_cast<EGLint>(width),
                                 EGL_HEIGHT,
                                 static_cast<EGLint>(height),
                                 EGL_LINUX_DRM_FOURCC_EXT,
                                 static_cast<EGLint>(drm_format)};
    int fd = CameraBufferManager::GetPlaneFd(buffer, plane);
    off_t offset = CameraBufferManager::GetPlaneOffset(buffer, plane);
    size_t stride = CameraBufferManager::GetPlaneStride(buffer, plane);
    attrs.push_back(static_cast<EGLint>(EGL_DMA_BUF_PLANE0_FD_EXT));
    attrs.push_back(fd);
    attrs.push_back(static_cast<EGLint>(EGL_DMA_BUF_PLANE0_OFFSET_EXT));
    attrs.push_back(offset);
    attrs.push_back(static_cast<EGLint>(EGL_DMA_BUF_PLANE0_PITCH_EXT));
    attrs.push_back(stride);
    attrs.push_back(EGL_NONE);
  
    EglImage image(attrs);
    image.width_ = width;
    image.height_ = height;
    return image;
  }
  
  // static
  EglImage EglImage::FromBuffer(buffer_handle_t buffer) {
    uint32_t width = CameraBufferManager::GetWidth(buffer);
    uint32_t height = CameraBufferManager::GetHeight(buffer);
    uint32_t drm_format = CameraBufferManager::GetDrmPixelFormat(buffer);
    std::vector<EGLint> attrs = {EGL_WIDTH,
                                 static_cast<EGLint>(width),
                                 EGL_HEIGHT,
                                 static_cast<EGLint>(height),
                                 EGL_LINUX_DRM_FOURCC_EXT,
                                 static_cast<EGLint>(drm_format)};
    CHECK_LE(CameraBufferManager::GetNumPlanes(buffer), 3);
    for (size_t plane = 0; plane < CameraBufferManager::GetNumPlanes(buffer);
         ++plane) {
      int fd = CameraBufferManager::GetPlaneFd(buffer, plane);
      off_t offset = CameraBufferManager::GetPlaneOffset(buffer, plane);
      size_t stride = CameraBufferManager::GetPlaneStride(buffer, plane);
      attrs.push_back(static_cast<EGLint>(EGL_DMA_BUF_PLANE0_FD_EXT + plane * 3));
      attrs.push_back(fd);
      attrs.push_back(
          static_cast<EGLint>(EGL_DMA_BUF_PLANE0_OFFSET_EXT + plane * 3));
      attrs.push_back(offset);
      attrs.push_back(
          static_cast<EGLint>(EGL_DMA_BUF_PLANE0_PITCH_EXT + plane * 3));
      attrs.push_back(stride);
    }
    if (IsPlanarYuv(drm_format)) {
      // TODO(jcliang): Allow specifying the following attributes.
      attrs.push_back(EGL_YUV_COLOR_SPACE_HINT_EXT);
      attrs.push_back(EGL_ITU_REC601_EXT);
      attrs.push_back(EGL_SAMPLE_RANGE_HINT_EXT);
      attrs.push_back(EGL_YUV_FULL_RANGE_EXT);
      attrs.push_back(EGL_YUV_CHROMA_HORIZONTAL_SITING_HINT_EXT);
      attrs.push_back(EGL_YUV_CHROMA_SITING_0_5_EXT);
      attrs.push_back(EGL_YUV_CHROMA_VERTICAL_SITING_HINT_EXT);
      attrs.push_back(EGL_YUV_CHROMA_SITING_0_5_EXT);
    }
    attrs.push_back(EGL_NONE);
  
    EglImage image(attrs);
    image.width_ = width;
    image.height_ = height;
    return image;
  }
  
  // static
  EglImage EglImage::FromDmaBufFds(const std::vector<DmaBufPlane>& planes,
                                   int width,
                                   int height,
                                   uint32_t drm_format) {
    std::vector<EGLint> attrs = {EGL_WIDTH,
                                 static_cast<EGLint>(width),
                                 EGL_HEIGHT,
                                 static_cast<EGLint>(height),
                                 EGL_LINUX_DRM_FOURCC_EXT,
                                 static_cast<EGLint>(drm_format)};
    for (size_t i = 0; i < planes.size(); ++i) {
      attrs.push_back(static_cast<EGLint>(EGL_DMA_BUF_PLANE0_FD_EXT + i * 3));
      attrs.push_back(planes[i].fd);
      attrs.push_back(static_cast<EGLint>(EGL_DMA_BUF_PLANE0_OFFSET_EXT + i * 3));
      attrs.push_back(planes[i].offset);
      attrs.push_back(static_cast<EGLint>(EGL_DMA_BUF_PLANE0_PITCH_EXT + i * 3));
      attrs.push_back(planes[i].stride);
    }
    attrs.push_back(EGL_NONE);
    EglImage image(attrs);
    image.width_ = width;
    image.height_ = height;
    return image;
  }
  
  EglImage::EglImage(EglImage&& other) {
    *this = std::move(other);
  }
  
  EglImage& EglImage::operator=(EglImage&& other) {
    if (this != &other) {
      Invalidate();
      image_ = other.image_;
      width_ = other.width_;
      height_ = other.height_;
  
      other.image_ = EGL_NO_IMAGE_KHR;
      other.width_ = 0;
      other.height_ = 0;
    }
    return *this;
  }
  
  EglImage::~EglImage() {
    Invalidate();
  }
  
  EglImage::EglImage(const std::vector<EGLint>& attribs) {
    if (!IsSupported()) {
      LOGF(ERROR) << "Creating EGLImageKHR is not supported";
      return;
    }
  
    image_ = g_eglCreateImageKHR(
        eglGetCurrentDisplay(), EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT,
        static_cast<EGLClientBuffer>(nullptr), attribs.data());
    EGLint error = eglGetError();
    if (error != EGL_SUCCESS) {
      LOGF(ERROR) << "Failed to create EGL image: " << EglGetErrorString(error);
    }
  }
  
  void EglImage::Invalidate() {
    if (IsValid()) {
      g_eglDestroyImageKHR(eglGetCurrentDisplay(), image_);
      image_ = EGL_NO_IMAGE_KHR;
    }
  }
  
  }  // namespace cros
  