// Copyright 2016 The Chromium 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 "flutter/flow/open_gl.h" #include "flutter/flow/texture_image.h" #include "flutter/glue/trace_event.h" #include "third_party/skia/include/gpu/gl/GrGLTypes.h" namespace flow { enum class TextureImageFormat { Grey, GreyAlpha, RGB, RGBA, }; enum class TextureImageDataFormat { UnsignedByte, UnsignedShort565, }; static inline GLint ToGLFormat(TextureImageFormat format) { switch (format) { case TextureImageFormat::RGBA: return GL_RGBA; case TextureImageFormat::RGB: return GL_RGB; case TextureImageFormat::Grey: return GL_LUMINANCE; case TextureImageFormat::GreyAlpha: return GL_LUMINANCE_ALPHA; } return GL_NONE; } static inline GLint ToGLDataFormat(TextureImageDataFormat dataFormat) { switch (dataFormat) { case TextureImageDataFormat::UnsignedByte: return GL_UNSIGNED_BYTE; case TextureImageDataFormat::UnsignedShort565: return GL_UNSIGNED_SHORT_5_6_5; } return GL_NONE; } static inline GrPixelConfig ToGrPixelConfig(TextureImageDataFormat dataFormat) { switch (dataFormat) { case TextureImageDataFormat::UnsignedByte: return kRGBA_8888_GrPixelConfig; case TextureImageDataFormat::UnsignedShort565: return kRGB_565_GrPixelConfig; } return kUnknown_GrPixelConfig; } static inline SkColorType ToSkColorType(TextureImageFormat format) { switch (format) { case TextureImageFormat::RGBA: return SkColorType::kRGBA_8888_SkColorType; case TextureImageFormat::RGB: case TextureImageFormat::Grey: case TextureImageFormat::GreyAlpha: // Add more specializations for greyscale images. return SkColorType::kRGB_565_SkColorType; } return kRGB_565_SkColorType; } static sk_sp TextureImageCreate(GrContext* context, TextureImageFormat format, const SkISize& size, TextureImageDataFormat dataFormat, const uint8_t* data) { TRACE_EVENT2("flutter", __func__, "width", size.width(), "height", size.height()); if (context == nullptr) { return nullptr; } GLuint handle = GL_NONE; // Generate the texture handle. glGenTextures(1, &handle); // Bind the texture. glBindTexture(GL_TEXTURE_2D, handle); // Specify default texture properties. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // Update unpack alignment based on format. glPixelStorei(GL_UNPACK_ALIGNMENT, dataFormat == TextureImageDataFormat::UnsignedByte ? 4 : 2); GLint gl_format = ToGLFormat(format); // Upload the texture. glTexImage2D(GL_TEXTURE_2D, // target 0, // level gl_format, // internal format size.fWidth, // width size.fHeight, // height 0, // border gl_format, // format ToGLDataFormat(dataFormat), // format data); // Clear the binding. We are done. glBindTexture(GL_TEXTURE_2D, GL_NONE); // Flush the texture before it can be bound by another thread. glFlush(); GrGLTextureInfo texInfo; texInfo.fTarget = GL_TEXTURE_2D; texInfo.fID = handle; // Create an SkImage handle from the texture. GrBackendTextureDesc desc; desc.fOrigin = kTopLeft_GrSurfaceOrigin; desc.fFlags = kNone_GrBackendTextureFlag; desc.fWidth = size.fWidth; desc.fHeight = size.fHeight; desc.fTextureHandle = reinterpret_cast(&texInfo); desc.fConfig = ToGrPixelConfig(dataFormat); if (auto image = SkImage::MakeFromAdoptedTexture(context, desc)) { // Texture handle was successfully adopted by the SkImage. return image; } // We could not create an SkImage from the texture. Since it could not be // adopted, delete the handle and return null. glDeleteTextures(1, &handle); return nullptr; } static sk_sp TextureImageCreate(GrContext* context, const SkBitmap& bitmap) { if (context == nullptr) { return nullptr; } if (bitmap.drawsNothing()) { return nullptr; } TextureImageDataFormat dataFormat = TextureImageDataFormat::UnsignedByte; TextureImageFormat imageFormat = TextureImageFormat::RGBA; switch (bitmap.colorType()) { case kRGB_565_SkColorType: dataFormat = TextureImageDataFormat::UnsignedShort565; imageFormat = TextureImageFormat::RGB; break; case kRGBA_8888_SkColorType: dataFormat = TextureImageDataFormat::UnsignedByte; imageFormat = TextureImageFormat::RGBA; break; default: // Add more as supported. return nullptr; } return TextureImageCreate( context, // context imageFormat, // image format SkISize::Make(bitmap.width(), bitmap.height()), // size dataFormat, // data format reinterpret_cast(bitmap.getPixels()) // data ); } sk_sp BitmapImageCreate(SkImageGenerator& generator) { SkBitmap bitmap; if (generator.tryGenerateBitmap(&bitmap)) { return SkImage::MakeFromBitmap(bitmap); } return nullptr; } sk_sp TextureImageCreate(GrContext* context, SkImageGenerator& generator) { if (context == nullptr) { return nullptr; } const SkImageInfo& info = generator.getInfo(); if (info.isEmpty()) { return nullptr; } TextureImageFormat imageFormat = TextureImageFormat::RGBA; bool preferOpaque = SkAlphaTypeIsOpaque(info.alphaType()); if (preferOpaque) { imageFormat = TextureImageFormat::RGB; } auto preferredImageInfo = SkImageInfo::Make(info.bounds().width(), // width info.bounds().height(), // height ToSkColorType(imageFormat), // color type preferOpaque ? SkAlphaType::kOpaque_SkAlphaType : SkAlphaType::kPremul_SkAlphaType); SkBitmap bitmap; { TRACE_EVENT1("flutter", "DecodePrimaryPreferrence", "Type", preferOpaque ? "RGB565" : "RGBA8888"); // Try our preferred config. if (generator.tryGenerateBitmap(&bitmap, preferredImageInfo, nullptr)) { // Our got our preferred bitmap. return TextureImageCreate(context, bitmap); } } { TRACE_EVENT0("flutter", "DecodeRecommended"); // Try the guessed config. if (generator.tryGenerateBitmap(&bitmap)) { return TextureImageCreate(context, bitmap); } } return nullptr; } } // namespace flow