55#include " flutter/shell/platform/darwin/ios/ios_external_texture_metal.h"
66
77#include " flutter/fml/logging.h"
8+ #include " third_party/skia/include/core/SkYUVAIndex.h"
89#include " third_party/skia/include/gpu/GrBackendSurface.h"
910#include " third_party/skia/include/gpu/GrDirectContext.h"
1011#include " third_party/skia/include/gpu/mtl/GrMtlTypes.h"
3536 auto pixel_buffer = fml::CFRef<CVPixelBufferRef>([external_texture_ copyPixelBuffer ]);
3637 if (!pixel_buffer) {
3738 pixel_buffer = std::move (last_pixel_buffer_);
39+ } else {
40+ pixel_format_ = CVPixelBufferGetPixelFormatType (pixel_buffer);
3841 }
3942
4043 // If the application told us there was a texture frame available but did not provide one when
6568 return nullptr ;
6669 }
6770
71+ sk_sp<SkImage> image = nullptr ;
72+ if (pixel_format_ == kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange ||
73+ pixel_format_ == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange ) {
74+ image = WrapNV12ExternalPixelBuffer (pixel_buffer, context);
75+ } else {
76+ image = WrapRGBAExternalPixelBuffer (pixel_buffer, context);
77+ }
78+
79+ if (!image) {
80+ FML_DLOG (ERROR) << " Could not wrap Metal texture as a Skia image." ;
81+ }
82+
83+ return image;
84+ }
85+
86+ sk_sp<SkImage> IOSExternalTextureMetal::WrapNV12ExternalPixelBuffer (
87+ fml::CFRef<CVPixelBufferRef> pixel_buffer,
88+ GrDirectContext* context) const {
6889 auto texture_size =
6990 SkISize::Make (CVPixelBufferGetWidth (pixel_buffer), CVPixelBufferGetHeight (pixel_buffer));
91+ CVMetalTextureRef y_metal_texture_raw = nullptr ;
92+ {
93+ auto cv_return =
94+ CVMetalTextureCacheCreateTextureFromImage (/* allocator=*/ kCFAllocatorDefault ,
95+ /* textureCache=*/ texture_cache_,
96+ /* sourceImage=*/ pixel_buffer,
97+ /* textureAttributes=*/ nullptr ,
98+ /* pixelFormat=*/ MTLPixelFormatR8Unorm ,
99+ /* width=*/ texture_size.width (),
100+ /* height=*/ texture_size.height (),
101+ /* planeIndex=*/ 0u ,
102+ /* texture=*/ &y_metal_texture_raw);
103+
104+ if (cv_return != kCVReturnSuccess ) {
105+ FML_DLOG (ERROR) << " Could not create Metal texture from pixel buffer: CVReturn " << cv_return;
106+ return nullptr ;
107+ }
108+ }
109+
110+ CVMetalTextureRef uv_metal_texture_raw = nullptr ;
111+ {
112+ auto cv_return =
113+ CVMetalTextureCacheCreateTextureFromImage (/* allocator=*/ kCFAllocatorDefault ,
114+ /* textureCache=*/ texture_cache_,
115+ /* sourceImage=*/ pixel_buffer,
116+ /* textureAttributes=*/ nullptr ,
117+ /* pixelFormat=*/ MTLPixelFormatRG8Unorm ,
118+ /* width=*/ texture_size.width () / 2 ,
119+ /* height=*/ texture_size.height () / 2 ,
120+ /* planeIndex=*/ 1u ,
121+ /* texture=*/ &uv_metal_texture_raw);
122+
123+ if (cv_return != kCVReturnSuccess ) {
124+ FML_DLOG (ERROR) << " Could not create Metal texture from pixel buffer: CVReturn " << cv_return;
125+ return nullptr ;
126+ }
127+ }
128+
129+ fml::CFRef<CVMetalTextureRef> y_metal_texture (y_metal_texture_raw);
130+
131+ GrMtlTextureInfo y_skia_texture_info;
132+ y_skia_texture_info.fTexture = sk_cf_obj<const void *>{
133+ [reinterpret_cast<NSObject *>(CVMetalTextureGetTexture (y_metal_texture)) retain ]};
70134
71- CVMetalTextureRef metal_texture_raw = NULL ;
135+ GrBackendTexture y_skia_backend_texture (/* width=*/ texture_size.width (),
136+ /* height=*/ texture_size.height (),
137+ /* mipMapped=*/ GrMipMapped ::kNo ,
138+ /* textureInfo=*/ y_skia_texture_info);
139+
140+ fml::CFRef<CVMetalTextureRef> uv_metal_texture (uv_metal_texture_raw);
141+
142+ GrMtlTextureInfo uv_skia_texture_info;
143+ uv_skia_texture_info.fTexture = sk_cf_obj<const void *>{
144+ [reinterpret_cast<NSObject *>(CVMetalTextureGetTexture (uv_metal_texture)) retain ]};
145+
146+ GrBackendTexture uv_skia_backend_texture (/* width=*/ texture_size.width (),
147+ /* height=*/ texture_size.height (),
148+ /* mipMapped=*/ GrMipMapped ::kNo ,
149+ /* textureInfo=*/ uv_skia_texture_info);
150+ GrBackendTexture nv12TextureHandles[] = {y_skia_backend_texture, uv_skia_backend_texture};
151+ SkYUVAIndex yuvaIndices[4 ] = {
152+ SkYUVAIndex{0 , SkColorChannel::kR }, // Read Y data from the red channel of the first texture
153+ SkYUVAIndex{1 , SkColorChannel::kR }, // Read U data from the red channel of the second texture
154+ SkYUVAIndex{1 ,
155+ SkColorChannel::kG }, // Read V data from the green channel of the second texture
156+ SkYUVAIndex{-1 , SkColorChannel::kA }}; // -1 means to omit the alpha data of YUVA
157+
158+ struct ImageCaptures {
159+ fml::CFRef<CVPixelBufferRef> buffer;
160+ fml::CFRef<CVMetalTextureRef> y_texture;
161+ fml::CFRef<CVMetalTextureRef> uv_texture;
162+ };
163+
164+ auto captures = std::make_unique<ImageCaptures>();
165+ captures->buffer = std::move (pixel_buffer);
166+ captures->y_texture = std::move (y_metal_texture);
167+ captures->uv_texture = std::move (uv_metal_texture);
168+
169+ SkImage::TextureReleaseProc release_proc = [](SkImage::ReleaseContext release_context) {
170+ auto captures = reinterpret_cast <ImageCaptures*>(release_context);
171+ delete captures;
172+ };
173+ sk_sp<SkImage> image = SkImage::MakeFromYUVATextures (
174+ context, kRec601_SkYUVColorSpace , nv12TextureHandles, yuvaIndices, texture_size,
175+ kTopLeft_GrSurfaceOrigin , /* imageColorSpace=*/ nullptr , release_proc, captures.release ());
176+ return image;
177+ }
178+
179+ sk_sp<SkImage> IOSExternalTextureMetal::WrapRGBAExternalPixelBuffer (
180+ fml::CFRef<CVPixelBufferRef> pixel_buffer,
181+ GrDirectContext* context) const {
182+ auto texture_size =
183+ SkISize::Make (CVPixelBufferGetWidth (pixel_buffer), CVPixelBufferGetHeight (pixel_buffer));
184+ CVMetalTextureRef metal_texture_raw = nullptr ;
72185 auto cv_return =
73- CVMetalTextureCacheCreateTextureFromImage (kCFAllocatorDefault , // allocator
74- texture_cache_, // texture cache
75- pixel_buffer, // source image
76- NULL , // texture attributes
77- MTLPixelFormatBGRA8Unorm , // pixel format
78- texture_size.width (), // width
79- texture_size.height (), // height
80- 0u , // plane index
81- &metal_texture_raw // [out] texture
82- );
186+ CVMetalTextureCacheCreateTextureFromImage (/* allocator=*/ kCFAllocatorDefault ,
187+ /* textureCache=*/ texture_cache_,
188+ /* sourceImage=*/ pixel_buffer,
189+ /* textureAttributes=*/ nullptr ,
190+ /* pixelFormat=*/ MTLPixelFormatBGRA8Unorm ,
191+ /* width=*/ texture_size.width (),
192+ /* height=*/ texture_size.height (),
193+ /* planeIndex=*/ 0u ,
194+ /* texture=*/ &metal_texture_raw);
83195
84196 if (cv_return != kCVReturnSuccess ) {
85197 FML_DLOG (ERROR) << " Could not create Metal texture from pixel buffer: CVReturn " << cv_return;
92204 skia_texture_info.fTexture = sk_cf_obj<const void *>{
93205 [reinterpret_cast<NSObject *>(CVMetalTextureGetTexture (metal_texture)) retain ]};
94206
95- GrBackendTexture skia_backend_texture (texture_size.width (), // width
96- texture_size.height (), // height
97- GrMipMapped ::kNo , // mip-mapped
98- skia_texture_info // texture info
99- );
207+ GrBackendTexture skia_backend_texture (/* width=*/ texture_size.width (),
208+ /* height=*/ texture_size.height (),
209+ /* mipMapped=*/ GrMipMapped ::kNo ,
210+ /* textureInfo=*/ skia_texture_info);
100211
101212 struct ImageCaptures {
102213 fml::CFRef<CVPixelBufferRef> buffer;
@@ -112,21 +223,12 @@ GrBackendTexture skia_backend_texture(texture_size.width(), // width
112223 delete captures;
113224 };
114225
115- auto image = SkImage::MakeFromTexture (context, // context
116- skia_backend_texture, // backend texture
117- kTopLeft_GrSurfaceOrigin , // origin
118- kBGRA_8888_SkColorType , // color type
119- kPremul_SkAlphaType , // alpha type
120- nullptr , // color space
121- release_proc, // release proc
122- captures.release () // release context
123-
124- );
125-
126- if (!image) {
127- FML_DLOG (ERROR) << " Could not wrap Metal texture as a Skia image." ;
128- }
226+ auto image =
227+ SkImage::MakeFromTexture (context, skia_backend_texture, kTopLeft_GrSurfaceOrigin ,
228+ kBGRA_8888_SkColorType , kPremul_SkAlphaType ,
229+ /* imageColorSpace=*/ nullptr , release_proc, captures.release ()
129230
231+ );
130232 return image;
131233}
132234
0 commit comments