219 lines
7.8 KiB
Objective-C
219 lines
7.8 KiB
Objective-C
#import "IMCCanvasView.h"
|
|
|
|
@interface IMCCanvasView ()
|
|
@property (nonatomic, assign) NSInteger clearColor;
|
|
@property (nonatomic, strong) NSMutableArray *commands;
|
|
@property (nonatomic, strong) UIImage *streamImage;
|
|
@end
|
|
|
|
@implementation IMCCanvasView
|
|
|
|
- (instancetype)initWithFrame:(CGRect)frame {
|
|
self = [super initWithFrame:frame];
|
|
if (self) {
|
|
_clearColor = 0;
|
|
_commands = [NSMutableArray array];
|
|
self.opaque = YES;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)clearWithColor:(NSInteger)color {
|
|
self.clearColor = color == 1 ? 1 : 0;
|
|
self.streamImage = nil;
|
|
[self.commands removeAllObjects];
|
|
[self setNeedsDisplay];
|
|
}
|
|
|
|
- (void)drawText:(NSString *)text x:(CGFloat)x y:(CGFloat)y size:(NSInteger)size {
|
|
if (!text) {
|
|
text = @"";
|
|
}
|
|
self.streamImage = nil;
|
|
[self.commands addObject:@{
|
|
@"type": @"text",
|
|
@"text": text,
|
|
@"x": @(x),
|
|
@"y": @(y),
|
|
@"size": @(size == 2 ? 2 : 1)
|
|
}];
|
|
[self setNeedsDisplay];
|
|
}
|
|
|
|
- (void)drawImageData:(NSData *)data x:(CGFloat)x y:(CGFloat)y {
|
|
UIImage *image = [UIImage imageWithData:data];
|
|
if (!image) {
|
|
return;
|
|
}
|
|
self.streamImage = nil;
|
|
[self.commands addObject:@{
|
|
@"type": @"image",
|
|
@"image": image,
|
|
@"x": @(x),
|
|
@"y": @(y)
|
|
}];
|
|
[self setNeedsDisplay];
|
|
}
|
|
|
|
- (void)displayMonochromeFrameBuffer:(NSData *)frameData {
|
|
if (frameData.length < 15000) {
|
|
return;
|
|
}
|
|
|
|
const NSUInteger width = 400;
|
|
const NSUInteger height = 300;
|
|
NSMutableData *rgba = [NSMutableData dataWithLength:width * height * 4];
|
|
const uint8_t *source = frameData.bytes;
|
|
uint8_t *pixels = rgba.mutableBytes;
|
|
|
|
for (NSUInteger y = 0; y < height; y++) {
|
|
NSUInteger invY = height - 1 - y;
|
|
for (NSUInteger x = 0; x < width; x++) {
|
|
NSUInteger bx = x / 2;
|
|
NSUInteger by = invY / 4;
|
|
NSUInteger index = bx * 75 + by;
|
|
NSUInteger bit = 7 - ((invY % 4) * 2 + (x % 2));
|
|
BOOL on = (source[index] & (1 << bit)) != 0;
|
|
uint8_t value = on ? 255 : 0;
|
|
NSUInteger pixelIndex = (y * width + x) * 4;
|
|
pixels[pixelIndex + 0] = value;
|
|
pixels[pixelIndex + 1] = value;
|
|
pixels[pixelIndex + 2] = value;
|
|
pixels[pixelIndex + 3] = 255;
|
|
}
|
|
}
|
|
|
|
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
|
CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)rgba);
|
|
CGImageRef cgImage = CGImageCreate(width,
|
|
height,
|
|
8,
|
|
32,
|
|
width * 4,
|
|
colorSpace,
|
|
kCGImageAlphaLast | kCGBitmapByteOrderDefault,
|
|
provider,
|
|
NULL,
|
|
false,
|
|
kCGRenderingIntentDefault);
|
|
if (cgImage) {
|
|
self.streamImage = [UIImage imageWithCGImage:cgImage];
|
|
[self.commands removeAllObjects];
|
|
[self setNeedsDisplay];
|
|
CGImageRelease(cgImage);
|
|
}
|
|
CGDataProviderRelease(provider);
|
|
CGColorSpaceRelease(colorSpace);
|
|
}
|
|
|
|
- (BOOL)displayRGB565FrameBuffer:(NSData *)frameData width:(NSUInteger)width height:(NSUInteger)height {
|
|
if (width == 0 || height == 0 || width > 2048 || height > 2048 ||
|
|
frameData.length != width * height * 2) {
|
|
return NO;
|
|
}
|
|
|
|
const uint8_t *source = frameData.bytes;
|
|
NSMutableData *rgba = [NSMutableData dataWithLength:width * height * 4];
|
|
uint8_t *pixels = rgba.mutableBytes;
|
|
for (NSUInteger i = 0; i < width * height; i++) {
|
|
// Network color frames use big-endian RGB565.
|
|
uint16_t value = ((uint16_t)source[i * 2] << 8) | source[i * 2 + 1];
|
|
uint8_t red5 = (value >> 11) & 0x1f;
|
|
uint8_t green6 = (value >> 5) & 0x3f;
|
|
uint8_t blue5 = value & 0x1f;
|
|
pixels[i * 4 + 0] = (red5 << 3) | (red5 >> 2);
|
|
pixels[i * 4 + 1] = (green6 << 2) | (green6 >> 4);
|
|
pixels[i * 4 + 2] = (blue5 << 3) | (blue5 >> 2);
|
|
pixels[i * 4 + 3] = 255;
|
|
}
|
|
|
|
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
|
CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)rgba);
|
|
CGImageRef cgImage = CGImageCreate(width,
|
|
height,
|
|
8,
|
|
32,
|
|
width * 4,
|
|
colorSpace,
|
|
kCGImageAlphaLast | kCGBitmapByteOrderDefault,
|
|
provider,
|
|
NULL,
|
|
false,
|
|
kCGRenderingIntentDefault);
|
|
if (cgImage) {
|
|
self.streamImage = [UIImage imageWithCGImage:cgImage];
|
|
[self.commands removeAllObjects];
|
|
[self setNeedsDisplay];
|
|
CGImageRelease(cgImage);
|
|
}
|
|
CGDataProviderRelease(provider);
|
|
CGColorSpaceRelease(colorSpace);
|
|
return cgImage != NULL;
|
|
}
|
|
|
|
- (NSString *)snapshotPNGBase64 {
|
|
UIGraphicsBeginImageContextWithOptions(self.bounds.size, YES, 0.0);
|
|
[self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES];
|
|
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
|
UIGraphicsEndImageContext();
|
|
|
|
NSData *pngData = UIImagePNGRepresentation(image);
|
|
return [pngData base64EncodedStringWithOptions:0];
|
|
}
|
|
|
|
- (CGPoint)pointForDeviceX:(CGFloat)x y:(CGFloat)y {
|
|
CGFloat scaleX = self.bounds.size.width / 400.0;
|
|
CGFloat scaleY = self.bounds.size.height / 300.0;
|
|
return CGPointMake(x * scaleX, y * scaleY);
|
|
}
|
|
|
|
- (void)drawRect:(CGRect)rect {
|
|
UIColor *background = self.clearColor == 1 ? [UIColor blackColor] : [UIColor whiteColor];
|
|
UIColor *foreground = self.clearColor == 1 ? [UIColor whiteColor] : [UIColor blackColor];
|
|
|
|
[background setFill];
|
|
UIRectFill(self.bounds);
|
|
|
|
if (self.streamImage) {
|
|
[self.streamImage drawInRect:self.bounds];
|
|
return;
|
|
}
|
|
|
|
CGContextRef context = UIGraphicsGetCurrentContext();
|
|
CGContextSetStrokeColorWithColor(context, foreground.CGColor);
|
|
CGContextSetLineWidth(context, 2.0);
|
|
CGContextStrokeRect(context, CGRectInset(self.bounds, 1.0, 1.0));
|
|
|
|
for (NSDictionary *command in self.commands) {
|
|
NSString *type = command[@"type"];
|
|
CGFloat x = [command[@"x"] doubleValue];
|
|
CGFloat y = [command[@"y"] doubleValue];
|
|
CGPoint point = [self pointForDeviceX:x y:y];
|
|
|
|
if ([type isEqualToString:@"text"]) {
|
|
NSInteger size = [command[@"size"] integerValue];
|
|
UIFont *font = [UIFont boldSystemFontOfSize:size == 2 ? 26.0 : 15.0];
|
|
NSDictionary *attrs = @{
|
|
NSFontAttributeName: font,
|
|
NSForegroundColorAttributeName: foreground
|
|
};
|
|
[command[@"text"] drawAtPoint:point withAttributes:attrs];
|
|
} else if ([type isEqualToString:@"image"]) {
|
|
UIImage *image = command[@"image"];
|
|
CGFloat maxWidth = self.bounds.size.width - point.x;
|
|
CGFloat maxHeight = self.bounds.size.height - point.y;
|
|
CGSize imageSize = image.size;
|
|
CGFloat scale = MIN(maxWidth / MAX(imageSize.width, 1.0),
|
|
maxHeight / MAX(imageSize.height, 1.0));
|
|
scale = MIN(scale, 1.0);
|
|
CGRect imageRect = CGRectMake(point.x,
|
|
point.y,
|
|
imageSize.width * scale,
|
|
imageSize.height * scale);
|
|
[image drawInRect:imageRect];
|
|
}
|
|
}
|
|
}
|
|
|
|
@end
|