QGVAPTextureLoader.m 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. // QGVAPTextureLoader.m
  2. // Tencent is pleased to support the open source community by making vap available.
  3. //
  4. // Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
  5. //
  6. // Licensed under the MIT License (the "License"); you may not use this file except in
  7. // compliance with the License. You may obtain a copy of the License at
  8. //
  9. // http://opensource.org/licenses/MIT
  10. //
  11. // Unless required by applicable law or agreed to in writing, software distributed under the License is
  12. // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
  13. // either express or implied. See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. #import "QGVAPTextureLoader.h"
  16. #import <MetalKit/MetalKit.h>
  17. #import "QGHWDShaderTypes.h"
  18. #import "QGVAPLogger.h"
  19. #import "UIDevice+VAPUtil.h"
  20. @implementation QGVAPTextureLoader
  21. #if TARGET_OS_SIMULATOR//模拟器
  22. + (id<MTLBuffer>)loadVapColorFillBufferWith:(UIColor *)color device:(id<MTLDevice>)device {return nil;}
  23. + (id<MTLTexture>)loadTextureWithImage:(UIImage *)image device:(id<MTLDevice>)device {return nil;}
  24. + (UIImage *)drawingImageForText:(NSString *)textStr color:(UIColor *)color size:(CGSize)size bold:(BOOL)bold {return nil;}
  25. + (UIFont *)getAppropriateFontWith:(NSString *)text rect:(CGRect)fitFrame designedSize:(CGFloat)designedFontSize bold:(BOOL)isBold textSize:(CGSize *)textSize {return nil;}
  26. #else
  27. + (id<MTLBuffer>)loadVapColorFillBufferWith:(UIColor *)color device:(id<MTLDevice>)device {
  28. CGFloat red = 0.0, green = 0.0, blue = 0.0, alpha = 0.0;
  29. if (color) {
  30. [color getRed:&red green:&green blue:&blue alpha:&alpha];
  31. }
  32. struct VapAttachmentFragmentParameter colorParams[] = {{color != nil ? 0 : 1, {red, green, blue, alpha}}};
  33. NSUInteger colorParamsSize = sizeof(struct VapAttachmentFragmentParameter);
  34. id<MTLBuffer> buffer = [device newBufferWithBytes:colorParams length:colorParamsSize options:kDefaultMTLResourceOption];
  35. return buffer;
  36. }
  37. + (id<MTLTexture>)loadTextureWithImage:(UIImage *)image device:(id<MTLDevice>)device {
  38. if (!image) {
  39. VAP_Error(kQGVAPModuleCommon, @"attemp to loadTexture with nil image");
  40. return nil;
  41. }
  42. if (@available(iOS 10.0, *)) {
  43. MTKTextureLoader *loader = [[MTKTextureLoader alloc] initWithDevice:device];
  44. NSError *error = nil;
  45. id<MTLTexture> texture = [loader newTextureWithCGImage:image.CGImage options:@{MTKTextureLoaderOptionOrigin : MTKTextureLoaderOriginFlippedVertically,MTKTextureLoaderOptionSRGB:@(NO)} error:&error];
  46. if (!texture || error) {
  47. VAP_Error(kQGVAPModuleCommon, @"loadTexture error:%@", error);
  48. return nil;
  49. }
  50. return texture;
  51. }
  52. return [self cg_loadTextureWithImage:image device:device];
  53. }
  54. + (UIImage *)drawingImageForText:(NSString *)textStr color:(UIColor *)color size:(CGSize)size bold:(BOOL)bold {
  55. if (textStr.length == 0) {
  56. VAP_Error(kQGVAPModuleCommon, @"draw text resource fail cuz text is nil !!");
  57. return nil;
  58. }
  59. if (!color) {
  60. color = [UIColor blackColor];
  61. }
  62. CGRect rect = CGRectMake(0, 0, size.width/2.0, size.height/2.0);
  63. CGSize textSize = CGSizeZero;
  64. UIFont *font = [QGVAPTextureLoader getAppropriateFontWith:textStr rect:rect designedSize:rect.size.height*0.8 bold:bold textSize:&textSize];
  65. if (!font) {
  66. VAP_Error(kQGVAPModuleCommon, @"draw text resource:%@ fail cuz font is nil !!", textStr);
  67. return nil;
  68. }
  69. NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
  70. paragraphStyle.alignment = NSTextAlignmentCenter;
  71. paragraphStyle.lineBreakMode = NSLineBreakByTruncatingTail;
  72. NSDictionary *attr = @{NSFontAttributeName:font, NSParagraphStyleAttributeName:paragraphStyle, NSForegroundColorAttributeName:color};
  73. UIGraphicsBeginImageContextWithOptions(rect.size, NO, [UIScreen mainScreen].scale);
  74. rect.origin.y = (rect.size.height - font.lineHeight)/2.0;
  75. [textStr drawWithRect:rect options:NSStringDrawingUsesLineFragmentOrigin attributes:attr context:nil];
  76. UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
  77. UIGraphicsEndImageContext();
  78. if (!image) {
  79. VAP_Error(kQGVAPModuleCommon, @"draw text resource:%@ fail cuz UIGraphics fail.", textStr);
  80. return nil;
  81. }
  82. return image;
  83. }
  84. + (id<MTLTexture>)cg_loadTextureWithImage:(UIImage *)image device:(id<MTLDevice>)device {
  85. CGImageRef imageRef = image.CGImage;
  86. if (!device || imageRef == nil) {
  87. VAP_Error(kQGVAPModuleCommon, @"load texture fail,cuz device/image is nil-device:%@ imaghe%@", device, imageRef);
  88. return nil;
  89. }
  90. CGFloat width = CGImageGetWidth(imageRef), height = CGImageGetHeight(imageRef);
  91. NSInteger bytesPerPixel = 4, bytesPerRow = bytesPerPixel * width, bitsPerComponent = 8;
  92. CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
  93. void *rawData = calloc(height * width * bytesPerPixel, sizeof(uint8_t));
  94. if (rawData == nil) {
  95. VAP_Error(kQGVAPModuleCommon, @"load texture fail,cuz alloc mem fail!width:%@ height:%@ bytesPerPixel:%@", @(width), @(height), @(bytesPerPixel));
  96. CGColorSpaceRelease(colorSpace);
  97. colorSpace = NULL;
  98. return nil;
  99. }
  100. CGContextRef context = CGBitmapContextCreate(rawData, width, height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast|kCGImageByteOrder32Big);
  101. CGColorSpaceRelease(colorSpace);
  102. colorSpace = NULL;
  103. if (context == nil) {
  104. VAP_Error(kQGVAPModuleCommon, @"CGBitmapContextCreate error width:%@ height:%@ bitsPerComponent:%@ bytesPerRow:%@", @(width), @(height), @(bitsPerComponent), @(bytesPerRow));
  105. free(rawData);
  106. return nil;
  107. }
  108. CGContextTranslateCTM(context, 0, height);
  109. CGContextScaleCTM(context, 1, -1);
  110. CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
  111. MTLTextureDescriptor *textureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA8Unorm width:width height:height mipmapped:NO];
  112. id<MTLTexture> texture = [device newTextureWithDescriptor:textureDescriptor];
  113. if (!texture) {
  114. VAP_Error(kQGVAPModuleCommon, @"load texture fail,cuz fail getting texture");
  115. free(rawData);
  116. CGContextRelease(context);
  117. return nil;
  118. }
  119. MTLRegion region = MTLRegionMake3D(0, 0, 0, width, height, 1);
  120. [texture replaceRegion:region mipmapLevel:0 withBytes:rawData bytesPerRow:bytesPerRow];
  121. free(rawData);
  122. CGContextRelease(context);
  123. return texture;
  124. }
  125. + (id<MTLTexture>)loadTextureWithData:(NSData *)data device:(id<MTLDevice>)device width:(CGFloat)width height:(CGFloat)height {
  126. if (!data) {
  127. VAP_Error(kQGVAPModuleCommon, @"attemp to loadTexture with nil data");
  128. return nil;
  129. }
  130. MTLTextureDescriptor *textureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatR8Unorm width:width height:height mipmapped:NO];
  131. id<MTLTexture> texture = [device newTextureWithDescriptor:textureDescriptor];
  132. if (!texture) {
  133. VAP_Error(kQGVAPModuleCommon, @"load texture fail,cuz fail getting texture");
  134. return nil;
  135. }
  136. MTLRegion region = MTLRegionMake3D(0, 0, 0, width, height, 1);
  137. const void *bytes = [data bytes];
  138. [texture replaceRegion:region mipmapLevel:0 withBytes:bytes bytesPerRow:width];
  139. return texture;
  140. }
  141. //根据指定的字符内容和容器大小计算合适的字体
  142. + (UIFont *)getAppropriateFontWith:(NSString *)text rect:(CGRect)fitFrame designedSize:(CGFloat)designedFontSize bold:(BOOL)isBold textSize:(CGSize *)textSize {
  143. UIFont *designedFont = isBold? [UIFont boldSystemFontOfSize:designedFontSize] : [UIFont systemFontOfSize:designedFontSize];
  144. if (text.length == 0 || CGRectEqualToRect(CGRectZero, fitFrame) || !designedFont) {
  145. *textSize = fitFrame.size;
  146. return designedFont ;
  147. }
  148. CGSize stringSize = [text sizeWithAttributes:@{NSFontAttributeName:designedFont}];
  149. CGFloat fontSize = designedFontSize;
  150. NSInteger remainExcuteCount = 100;
  151. while (stringSize.width > fitFrame.size.width && fontSize > 2.0 && remainExcuteCount > 0) {
  152. fontSize *= 0.9;
  153. remainExcuteCount -= 1;
  154. designedFont = isBold? [UIFont boldSystemFontOfSize:fontSize] : [UIFont systemFontOfSize:fontSize];
  155. stringSize = [text sizeWithAttributes:@{NSFontAttributeName:designedFont}];
  156. }
  157. if (remainExcuteCount < 1 || fontSize < 5.0) {
  158. VAP_Event(kQGVAPModuleCommon, @"data exception content:%@ rect:%@ designedSize:%@ isBold:%@", text, [NSValue valueWithCGRect:fitFrame], @(designedFontSize), @(isBold));
  159. }
  160. *textSize = stringSize;
  161. return designedFont;
  162. }
  163. #endif
  164. @end