Skip to content
Snippets Groups Projects
Commit 87352a1d authored by George Nachman's avatar George Nachman
Browse files

Implement a really damn elegant solution to subpixel antialiasing in the GPU...

Implement a really damn elegant solution to subpixel antialiasing in the GPU renderer, if I don't say so myself.

- Add a frame cursor renderer. Pass screen scale through to all transient states.
- Use a single value for gridsize between frameData and perFrameState
- Subpixel color channels are completely independent. Change models to be keyed on a pair of colors, not a combo of six.  Get bilinear interpolation working! YAY!
- Refactor here and there
- Move inputs to transient state into a new class so it's easier to pass new data to renderers' transient states
- Fork the text fragment shader into a blending and nonblending version
- Draw background image to a temporary texture so the text renderer can sample its background color to do subpixel aa
- Alpha blend default background color over background image
parent 5f892dc5
No related branches found
No related tags found
No related merge requests found
Loading
Loading
@@ -8,12 +8,12 @@ extern "C" {
#import "iTermSubpixelModelBuilder.h"
#import "iTermTextureArray.h"
#import "iTermTextureMap.h"
#import "NSDictionary+iTerm.h"
#import <unordered_map>
 
@interface iTermTextRendererTransientState ()
 
@property (nonatomic, readonly) NSIndexSet *indexes;
@property (nonatomic, strong) NSData *subpixelModelData;
@property (nonatomic, weak) iTermTextureMap *textureMap;
- (void)addIndex:(NSInteger)index;
 
Loading
Loading
@@ -22,8 +22,6 @@ extern "C" {
@implementation iTermTextRendererTransientState {
iTermTextureMapStage *_stage;
NSMutableIndexSet *_indexes;
NSMutableArray<iTermSubpixelModel *> *_models;
std::unordered_map<NSUInteger, NSUInteger> *_modelMap; // Maps a 48 bit fg/bg color to an index into _models.
dispatch_group_t _group;
id<MTLCommandBuffer> _commandBuffer;
}
Loading
Loading
@@ -32,19 +30,12 @@ extern "C" {
self = [super init];
if (self) {
_indexes = [NSMutableIndexSet indexSet];
_models = [NSMutableArray array];
_modelMap = new std::unordered_map<NSUInteger, NSUInteger>();
_group = dispatch_group_create();
}
return self;
}
 
- (void)dealloc {
delete _modelMap;
}
- (void)willDraw {
self.subpixelModelData = [self newSubpixelModelData];
[_stage blitNewTexturesFromStagingAreaWithCommandBuffer:_commandBuffer];
}
 
Loading
Loading
@@ -57,17 +48,22 @@ extern "C" {
}];
}
 
- (NSUInteger)sizeOfNewPIUBuffer {
return sizeof(iTermTextPIU) * self.cellConfiguration.gridSize.width * self.cellConfiguration.gridSize.height;
}
- (void)setGlyphKeysData:(NSData *)glyphKeysData
attributesData:(NSData *)attributesData
row:(int)row
creation:(NSImage *(NS_NOESCAPE ^)(int x))creation {
const int width = self.gridSize.width;
const int width = self.cellConfiguration.gridSize.width;
const iTermMetalGlyphKey *glyphKeys = (iTermMetalGlyphKey *)glyphKeysData.bytes;
const iTermMetalGlyphAttributes *attributes = (iTermMetalGlyphAttributes *)attributesData.bytes;
const float w = 1.0 / _textureMap.array.atlasSize.width;
const float h = 1.0 / _textureMap.array.atlasSize.height;
iTermTextureArray *array = _textureMap.array;
iTermTextPIU *pius = [self textPIUs];
iTermTextPIU *pius = (iTermTextPIU *)self.pius.contents;
 
NSInteger lastIndex = 0;
for (int x = 0; x < width; x++) {
Loading
Loading
@@ -80,12 +76,12 @@ extern "C" {
creation:creation];
}
if (index >= 0) {
// Update the PIU with the session index. It may not be a legit value yet.
const size_t i = x + row * self.gridSize.width;
const size_t i = x + row * self.cellConfiguration.gridSize.width;
iTermTextPIU *piu = &pius[i];
MTLOrigin origin = [array offsetForIndex:index];
piu->textureOffset = (vector_float2){ origin.x * w, origin.y * h };
piu->colorModelIndex = [self colorModelIndexForAttributes:&attributes[x]];
piu->textColor = attributes[x].foregroundColor;
piu->backgroundColor = attributes[x].backgroundColor;
[self addIndex:index];
}
lastIndex = index;
Loading
Loading
@@ -97,30 +93,22 @@ extern "C" {
_stage = nil;
}
 
- (iTermTextPIU *)textPIUs {
return (iTermTextPIU *)self.modelData.mutableBytes;
}
- (nonnull NSMutableData *)modelData {
if (_modelData == nil) {
_modelData = [[NSMutableData alloc] initWithLength:sizeof(iTermTextPIU) * self.gridSize.width * self.gridSize.height];
_modelData = [[NSMutableData alloc] initWithLength:sizeof(iTermTextPIU) * self.cellConfiguration.gridSize.width * self.cellConfiguration.gridSize.height];
}
return _modelData;
}
 
- (void)initializePIUData {
iTermTextPIU *pius = self.textPIUs;
- (void)initializePIUBytes:(void *)bytes {
iTermTextPIU *pius = (iTermTextPIU *)bytes;
NSInteger i = 0;
for (NSInteger y = 0; y < self.gridSize.height; y++) {
for (NSInteger x = 0; x < self.gridSize.width; x++) {
const iTermTextPIU uniform = {
.offset = {
static_cast<float>(x * self.cellSize.width),
static_cast<float>((self.gridSize.height - y - 1) * self.cellSize.height)
},
.textureOffset = { 0, 0 }
};
memcpy(&pius[i++], &uniform, sizeof(uniform));
#warning TODO: There is no reason to do this unless the grid size changes.
for (NSInteger y = 0; y < self.cellConfiguration.gridSize.height; y++) {
const float yOffset = (self.cellConfiguration.gridSize.height - y - 1) * self.cellConfiguration.cellSize.height;
for (NSInteger x = 0; x < self.cellConfiguration.gridSize.width; x++) {
pius[i++].offset = simd_make_float2(x * self.cellConfiguration.cellSize.width,
yOffset);
}
}
}
Loading
Loading
@@ -129,35 +117,6 @@ extern "C" {
[_indexes addIndex:index];
}
 
- (NSData *)newSubpixelModelData {
const size_t tableSize = _models.firstObject.table.length;
NSMutableData *data = [NSMutableData dataWithLength:_models.count * tableSize];
unsigned char *output = (unsigned char *)data.mutableBytes;
[_models enumerateObjectsUsingBlock:^(iTermSubpixelModel * _Nonnull model, NSUInteger idx, BOOL * _Nonnull stop) {
const size_t offset = idx * tableSize;
memcpy(output + offset, model.table.bytes, tableSize);
}];
return data;
}
- (int)colorModelIndexForAttributes:(const iTermMetalGlyphAttributes *)attributes {
NSUInteger key = [iTermSubpixelModel keyForForegroundColor:attributes->foregroundColor
backgroundColor:attributes->backgroundColor];
auto it = _modelMap->find(key);
if (it == _modelMap->end()) {
// TODO: Expire old models
const NSUInteger index = _models.count;
iTermSubpixelModel *model = [[iTermSubpixelModelBuilder sharedInstance] modelForForegoundColor:attributes->foregroundColor
backgroundColor:attributes->backgroundColor];
[_models addObject:model];
(*_modelMap)[model.key] = index;
DLog(@"Assign model %@ to index %@", model, @(index));
return index;
} else {
return it->second;
}
}
#pragma mark - Debugging
 
- (iTermTextPIU *)piuArray {
Loading
Loading
@@ -173,6 +132,29 @@ extern "C" {
@implementation iTermTextRenderer {
iTermMetalCellRenderer *_cellRenderer;
iTermTextureMap *_textureMap;
id<MTLBuffer> _models;
}
- (id<MTLBuffer>)subpixelModels {
if (_models == nil) {
NSMutableData *data = [NSMutableData data];
// The fragment function assumes we use the value 17 here. It's
// convenient that 17 evenly divides 255 (17 * 15 = 255).
float stride = 255.0/17.0;
int i = 0;
for (float textColor = 0; textColor < 256; textColor += stride) {
for (float backgroundColor = 0; backgroundColor < 256; backgroundColor += stride) {
iTermSubpixelModel *model = [[iTermSubpixelModelBuilder sharedInstance] modelForForegoundColor:MIN(MAX(0, textColor / 255.0), 1)
backgroundColor:MIN(MAX(0, backgroundColor / 255.0), 1)];
[data appendData:model.table];
}
}
#warning TODO: Only create one per device
_models = [_cellRenderer.device newBufferWithBytes:data.bytes
length:data.length
options:MTLResourceStorageModeShared];
}
return _models;
}
 
- (instancetype)initWithDevice:(id<MTLDevice>)device {
Loading
Loading
@@ -192,31 +174,28 @@ extern "C" {
return _textureMap.haveStageAvailable;
}
 
- (void)createTransientStateForViewportSize:(vector_uint2)viewportSize
cellSize:(CGSize)cellSize
gridSize:(VT100GridSize)gridSize
commandBuffer:(id<MTLCommandBuffer>)commandBuffer
completion:(void (^)(__kindof iTermMetalCellRendererTransientState * _Nonnull))completion {
[_cellRenderer createTransientStateForViewportSize:viewportSize
cellSize:cellSize
gridSize:gridSize
commandBuffer:commandBuffer
completion:^(__kindof iTermMetalCellRendererTransientState * _Nonnull transientState) {
[self initializeTransientState:transientState
commandBuffer:commandBuffer
completion:completion];
}];
- (void)createTransientStateForCellConfiguration:(iTermCellRenderConfiguration *)configuration
commandBuffer:(id<MTLCommandBuffer>)commandBuffer
completion:(void (^)(__kindof iTermMetalRendererTransientState * _Nonnull))completion {
_cellRenderer.fragmentFunctionName = configuration.usingIntermediatePass ? @"iTermTextFragmentShaderWithBlending" : @"iTermTextFragmentShaderSolidBackground";
[_cellRenderer createTransientStateForCellConfiguration:configuration
commandBuffer:commandBuffer
completion:^(__kindof iTermMetalCellRendererTransientState * _Nonnull transientState) {
[self initializeTransientState:transientState
commandBuffer:commandBuffer
completion:completion];
}];
}
 
- (void)initializeTransientState:(iTermTextRendererTransientState *)tState
commandBuffer:(id<MTLCommandBuffer>)commandBuffer
completion:(void (^)(__kindof iTermMetalCellRendererTransientState * _Nonnull))completion {
const NSInteger capacity = tState.gridSize.width * tState.gridSize.height * 2;
const NSInteger capacity = tState.cellConfiguration.gridSize.width * tState.cellConfiguration.gridSize.height * 2;
if (_textureMap == nil ||
!CGSizeEqualToSize(_textureMap.cellSize, tState.cellSize) ||
!CGSizeEqualToSize(_textureMap.cellSize, tState.cellConfiguration.cellSize) ||
_textureMap.capacity != capacity) {
_textureMap = [[iTermTextureMap alloc] initWithDevice:_cellRenderer.device
cellSize:tState.cellSize
cellSize:tState.cellConfiguration.cellSize
capacity:capacity];
_textureMap.label = [NSString stringWithFormat:@"[texture map for %p]", self];
_textureMap.array.texture.label = @"Texture grid for session";
Loading
Loading
@@ -225,8 +204,10 @@ extern "C" {
 
// The vertex buffer's texture coordinates depend on the texture map's atlas size so it must
// be initialized after the texture map.
tState.vertexBuffer = [self newQuadOfSize:tState.cellSize];
[tState initializePIUData];
tState.vertexBuffer = [self newQuadOfSize:tState.cellConfiguration.cellSize];
tState.pius = [_cellRenderer.device newBufferWithLength:tState.sizeOfNewPIUBuffer
options:MTLResourceStorageModeShared];
[tState initializePIUBytes:tState.pius.contents];
 
[tState prepareForDrawWithCommandBuffer:commandBuffer completion:^{
completion(tState);
Loading
Loading
@@ -258,25 +239,23 @@ extern "C" {
- (void)drawWithRenderEncoder:(id<MTLRenderCommandEncoder>)renderEncoder
transientState:(__kindof iTermMetalCellRendererTransientState *)transientState {
iTermTextRendererTransientState *tState = transientState;
tState.pius = [_cellRenderer.device newBufferWithBytes:tState.modelData.mutableBytes
length:tState.modelData.length
options:MTLResourceStorageModeShared];
tState.vertexBuffer.label = @"text vertex buffer";
tState.pius.label = @"text PIUs";
tState.offsetBuffer.label = @"text offset";
 
id<MTLBuffer> subpixelModels = [_cellRenderer.device newBufferWithBytes:tState.subpixelModelData.bytes
length:tState.subpixelModelData.length
options:MTLResourceStorageModeShared];
NSDictionary *textures = @{ @(iTermTextureIndexPrimary): tState.textureMap.array.texture };
if (tState.cellConfiguration.usingIntermediatePass) {
textures = [textures dictionaryBySettingObject:tState.backgroundTexture forKey:@(iTermTextureIndexBackground)];
}
[_cellRenderer drawWithTransientState:tState
renderEncoder:renderEncoder
numberOfVertices:6
numberOfPIUs:tState.gridSize.width * tState.gridSize.height
numberOfPIUs:tState.cellConfiguration.gridSize.width * tState.cellConfiguration.gridSize.height
vertexBuffers:@{ @(iTermVertexInputIndexVertices): tState.vertexBuffer,
@(iTermVertexInputIndexPerInstanceUniforms): tState.pius,
@(iTermVertexInputIndexOffset): tState.offsetBuffer }
fragmentBuffers:@{ @(iTermFragmentBufferIndexColorModels): subpixelModels }
textures:@{ @(iTermTextureIndexPrimary): tState.textureMap.array.texture }];
fragmentBuffers:@{ @(iTermFragmentBufferIndexColorModels): self.subpixelModels }
textures:textures];
}
 
@end
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
#import "iTermShaderTypes.h"
typedef struct {
float4 clipSpacePosition [[position]];
float2 textureCoordinate;
} iTermCopyBackgroundVertexFunctionOutput;
vertex iTermCopyBackgroundVertexFunctionOutput
iTermCopyBackgroundVertexShader(uint vertexID [[ vertex_id ]],
constant iTermVertex *vertexArray [[ buffer(iTermVertexInputIndexVertices) ]],
constant vector_uint2 *viewportSizePointer [[ buffer(iTermVertexInputIndexViewportSize) ]]) {
iTermCopyBackgroundVertexFunctionOutput out;
float2 pixelSpacePosition = vertexArray[vertexID].position.xy;
float2 viewportSize = float2(*viewportSizePointer);
out.clipSpacePosition.xy = pixelSpacePosition / viewportSize;
out.clipSpacePosition.z = 0.0;
out.clipSpacePosition.w = 1;
out.textureCoordinate = vertexArray[vertexID].textureCoordinate;
return out;
}
fragment float4
iTermCopyBackgroundFragmentShader(iTermCopyBackgroundVertexFunctionOutput in [[stage_in]],
texture2d<half> texture [[ texture(iTermTextureIndexPrimary) ]]) {
constexpr sampler textureSampler (mag_filter::linear,
min_filter::linear);
const half4 colorSample = texture.sample(textureSampler, in.textureCoordinate);
return float4(colorSample);
}
Loading
Loading
@@ -13,10 +13,13 @@ typedef enum iTermVertexInputIndex {
 
typedef enum iTermTextureIndex {
iTermTextureIndexPrimary = 0,
// A texture containing the background we're drawing over.
iTermTextureIndexBackground = 1
} iTermTextureIndex;
 
typedef enum {
iTermFragmentBufferIndexColorModels = 1 // Array of triples of 256-byte color tables in rgb order
iTermFragmentBufferIndexColorModels = 1 // Array of 256-byte color tables
} iTermFragmentBufferIndex;
 
typedef struct {
Loading
Loading
@@ -34,7 +37,9 @@ typedef struct {
// Offset of source texture
vector_float2 textureOffset;
 
int colorModelIndex;
// Values in 0-1. These will be composited over what's already rendered.
vector_float4 backgroundColor;
vector_float4 textColor;
} iTermTextPIU;
 
typedef struct {
Loading
Loading
Loading
Loading
@@ -8,7 +8,9 @@ using namespace metal;
typedef struct {
float4 clipSpacePosition [[position]];
float2 textureCoordinate;
int colorModelIndex;
float2 backgroundTextureCoordinate;
float4 textColor;
float4 backgroundColor;
} iTermTextVertexFunctionOutput;
 
vertex iTermTextVertexFunctionOutput
Loading
Loading
@@ -27,40 +29,236 @@ iTermTextVertexShader(uint vertexID [[ vertex_id ]],
out.clipSpacePosition.z = 0.0;
out.clipSpacePosition.w = 1;
 
out.backgroundTextureCoordinate = pixelSpacePosition / viewportSize;
out.backgroundTextureCoordinate.y = 1 - out.backgroundTextureCoordinate.y;
out.textureCoordinate = vertexArray[vertexID].textureCoordinate + perInstanceUniforms[iid].textureOffset;
out.colorModelIndex = perInstanceUniforms[iid].colorModelIndex;
out.textColor = perInstanceUniforms[iid].textColor;
out.backgroundColor = perInstanceUniforms[iid].backgroundColor;
 
return out;
}
 
fragment float4
iTermTextFragmentShader(iTermTextVertexFunctionOutput in [[stage_in]],
texture2d<half> texture [[ texture(iTermTextureIndexPrimary) ]],
constant unsigned char *colorModels [[ buffer(iTermFragmentBufferIndexColorModels) ]]) {
iTermTextFragmentShaderSolidBackground(iTermTextVertexFunctionOutput in [[stage_in]],
texture2d<half> texture [[ texture(iTermTextureIndexPrimary) ]],
constant unsigned char *colorModels [[ buffer(iTermFragmentBufferIndexColorModels) ]]) {
constexpr sampler textureSampler(mag_filter::linear,
min_filter::linear);
 
const half4 bwColor = texture.sample(textureSampler, in.textureCoordinate);
 
if (bwColor.x == 1 && bwColor.y == 1 && bwColor.z == 1) {
// TODO
// This doesn't draw the background when the pixel doesn't contain any of the glyph.
// But this will look terrible over a background image, diagonal stripes, or badge. I should
// build a fixed number of color models that evenly sample the space of possible color
// models, then sample the color in the drawable and have the fragment shader interpolate
// between the nearest models for the background color of this pixel.
discard_fragment();
}
const short4 bwIntColor = static_cast<short4>(bwColor * 255);
const short4 bwIntIndices = bwIntColor * 3 + short4(0, 1, 2, 0);
// Base index for this color model
const int i = in.colorModelIndex * 256 * 3;
// Find RGB values to map colors in the black-on-white glyph to
const uchar4 rgba = uchar4(colorModels[i + bwIntIndices.x],
colorModels[i + bwIntIndices.y],
colorModels[i + bwIntIndices.z],
255);
return static_cast<float4>(rgba) / 255;
// For a discussion of this code, see this document:
// https://docs.google.com/document/d/1vfBq6vg409Zky-IQ7ne-Yy7olPtVCl0dq3PG20E8KDs
// The formulas for bilinear interpolation came from:
// https://en.wikipedia.org/wiki/Bilinear_interpolation
//
// The goal is to estimate the color of a pixel at P. We have to find the correct remapping
// table for this cell's foreground/background color. The x axis is the text color and the
// y axis is the background color.
//
// We'll find the four remapping tables that are closest to representing the text/background
// color at this cell. We'll look up the black-and-white glyph's color for this pixel in those
// in those four remapping tables. Then we'll use bilinear interpolation to come up with an
// estimate of what color to output.
//
// From a random sample of 1000 text/bg color combinations this gets within 2.3/255 of the
// correct color in the worst case.
//
// TODO: Ask someone smart if there's a more efficient way to do this.
//
// | Q12 Q22
// y2 |..*...............*...........
// | : : :
// | : :P :
// y |..........*...................
// | : : :
// | : : :
// | :Q11 : :Q21
// y1 |..*...............*...........
// | : : :
// | : : :
// +---------------------------------
// x1 x x2
// Get text and background color in [0, 255]
float4 x = in.textColor * 255.0;
// TODO: Sample the background color from the drawable and blend it if needed.
float4 y = in.backgroundColor * 255.0;
// Indexes to lower and upper neighbors for x in [0, 17]
int4 x1i = static_cast<int4>(floor(x / 15.0));
int4 x2i = min(17, x1i + 1);
// Make sure x1i != x2i
x1i = max(0, x2i - 1);
// Values of lower and upper neighbors for x in [0, 255]
float4 x1 = static_cast<float4>(x1i) * 15.0;
float4 x2 = static_cast<float4>(x2i) * 15.0;
// Indexes to lower and upper neighbors for y in [0, 17]
int4 y1i = static_cast<int4>(floor(y / 15.0));
int4 y2i = min(17, y1i + 1);
// Make sure y1i != y2i
y1i = max(0, y2i - 1);
// Values of lower and upper neighbors for y in [0, 255]
float4 y1 = static_cast<float4>(y1i) * 15.0;
float4 y2 = static_cast<float4>(y2i) * 15.0;
// Index into tables (x,y,z ~ index for r,g,b)
int4 i = static_cast<int4>(round(bwColor * 255));
// indexes to use to look up f(Q_y_x)
// 18 is the multiplier because the color models are quantized to the color in [0,255] / 17.0 and
// there's an off-by-one thing going on here.
int4 fq11i = 256 * (x1i * 18 + y1i) + i;
int4 fq12i = 256 * (x1i * 18 + y2i) + i;
int4 fq21i = 256 * (x2i * 18 + y1i) + i;
int4 fq22i = 256 * (x2i * 18 + y2i) + i;
// Four neighbors' values in [0, 255]. The vectors' x,y,z correspond to red, green, and blue.
float4 fq11 = float4(colorModels[fq11i.x],
colorModels[fq11i.y],
colorModels[fq11i.z],
255);
float4 fq12 = float4(colorModels[fq12i.x],
colorModels[fq12i.y],
colorModels[fq12i.z],
255);
float4 fq21 = float4(colorModels[fq21i.x],
colorModels[fq21i.y],
colorModels[fq21i.z],
255);
float4 fq22 = float4(colorModels[fq22i.x],
colorModels[fq22i.y],
colorModels[fq22i.z],
255);
// Do bilinear interpolation on the r, g, and b values simultaneously.
float4 f_x_y1 = (x2 - x) / (x2 - x1) * fq11 + (x - x1) / (x2 - x1) * fq21;
float4 f_x_y2 = (x2 - x) / (x2 - x1) * fq12 + (x - x1) / (x2 - x1) * fq22;
float4 f_x_y = (y2 - y) / (y2 - y1) * f_x_y1 + (y - y1) / (y2 - y1) * f_x_y2;
return float4(f_x_y.x,
f_x_y.y,
f_x_y.z,
255) / 255.0;
}
fragment float4
iTermTextFragmentShaderWithBlending(iTermTextVertexFunctionOutput in [[stage_in]],
texture2d<half> texture [[ texture(iTermTextureIndexPrimary) ]],
texture2d<half> drawable [[ texture(iTermTextureIndexBackground) ]],
constant unsigned char *colorModels [[ buffer(iTermFragmentBufferIndexColorModels) ]]) {
constexpr sampler textureSampler(mag_filter::linear,
min_filter::linear);
const half4 bwColor = texture.sample(textureSampler, in.textureCoordinate);
if (bwColor.x == 1 && bwColor.y == 1 && bwColor.z == 1) {
discard_fragment();
}
// For a discussion of this code, see this document:
// https://docs.google.com/document/d/1vfBq6vg409Zky-IQ7ne-Yy7olPtVCl0dq3PG20E8KDs
// The formulas for bilinear interpolation came from:
// https://en.wikipedia.org/wiki/Bilinear_interpolation
//
// The goal is to estimate the color of a pixel at P. We have to find the correct remapping
// table for this cell's foreground/background color. The x axis is the text color and the
// y axis is the background color.
//
// We'll find the four remapping tables that are closest to representing the text/background
// color at this cell. We'll look up the black-and-white glyph's color for this pixel in those
// in those four remapping tables. Then we'll use bilinear interpolation to come up with an
// estimate of what color to output.
//
// From a random sample of 1000 text/bg color combinations this gets within 2.3/255 of the
// correct color in the worst case.
//
// TODO: Ask someone smart if there's a more efficient way to do this.
//
// | Q12 Q22
// y2 |..*...............*...........
// | : : :
// | : :P :
// y |..........*...................
// | : : :
// | : : :
// | :Q11 : :Q21
// y1 |..*...............*...........
// | : : :
// | : : :
// +---------------------------------
// x1 x x2
// Get text and background color in [0, 255]
float4 x = in.textColor * 255.0;
float4 y = static_cast<float4>(drawable.sample(textureSampler, in.backgroundTextureCoordinate)) * 255.0;
// Indexes to lower and upper neighbors for x in [0, 17]
int4 x1i = static_cast<int4>(floor(x / 15.0));
int4 x2i = min(17, x1i + 1);
// Make sure x1i != x2i
x1i = max(0, x2i - 1);
// Values of lower and upper neighbors for x in [0, 255]
float4 x1 = static_cast<float4>(x1i) * 15.0;
float4 x2 = static_cast<float4>(x2i) * 15.0;
// Indexes to lower and upper neighbors for y in [0, 17]
int4 y1i = static_cast<int4>(floor(y / 15.0));
int4 y2i = min(17, y1i + 1);
// Make sure y1i != y2i
y1i = max(0, y2i - 1);
// Values of lower and upper neighbors for y in [0, 255]
float4 y1 = static_cast<float4>(y1i) * 15.0;
float4 y2 = static_cast<float4>(y2i) * 15.0;
// Index into tables (x,y,z ~ index for r,g,b)
int4 i = static_cast<int4>(round(bwColor * 255));
// indexes to use to look up f(Q_y_x)
// 18 is the multiplier because the color models are quantized to the color in [0,255] / 17.0 and
// there's an off-by-one thing going on here.
int4 fq11i = 256 * (x1i * 18 + y1i) + i;
int4 fq12i = 256 * (x1i * 18 + y2i) + i;
int4 fq21i = 256 * (x2i * 18 + y1i) + i;
int4 fq22i = 256 * (x2i * 18 + y2i) + i;
// Four neighbors' values in [0, 255]. The vectors' x,y,z correspond to red, green, and blue.
float4 fq11 = float4(colorModels[fq11i.x],
colorModels[fq11i.y],
colorModels[fq11i.z],
255);
float4 fq12 = float4(colorModels[fq12i.x],
colorModels[fq12i.y],
colorModels[fq12i.z],
255);
float4 fq21 = float4(colorModels[fq21i.x],
colorModels[fq21i.y],
colorModels[fq21i.z],
255);
float4 fq22 = float4(colorModels[fq22i.x],
colorModels[fq22i.y],
colorModels[fq22i.z],
255);
// Do bilinear interpolation on the r, g, and b values simultaneously.
float4 f_x_y1 = (x2 - x) / (x2 - x1) * fq11 + (x - x1) / (x2 - x1) * fq21;
float4 f_x_y2 = (x2 - x) / (x2 - x1) * fq12 + (x - x1) / (x2 - x1) * fq22;
float4 f_x_y = (y2 - y) / (y2 - y1) * f_x_y1 + (y - y1) / (y2 - y1) * f_x_y2;
return float4(f_x_y.x,
f_x_y.y,
f_x_y.z,
255) / 255.0;
}
 
Loading
Loading
@@ -25,6 +25,8 @@ NS_ASSUME_NONNULL_BEGIN
 
@protocol iTermMetalDriverDataSourcePerFrameState<NSObject>
 
@property (nonatomic, readonly) VT100GridSize gridSize;
- (void)metalGetGlyphKeys:(iTermMetalGlyphKey *)glyphKeys
attributes:(iTermMetalGlyphAttributes *)attributes
background:(vector_float4 *)backgrounds
Loading
Loading
@@ -36,6 +38,10 @@ NS_ASSUME_NONNULL_BEGIN
- (NSImage *)metalImageForGlyphKey:(iTermMetalGlyphKey *)glyphKey
size:(CGSize)size
scale:(CGFloat)scale;
// Returns the background image or nil. If there's a background image, fill in blending and tiled.
- (NSImage *)metalBackgroundImageGetBlending:(CGFloat *)blending tiled:(BOOL *)tiled;
@end
 
@protocol iTermMetalDriverDataSource<NSObject>
Loading
Loading
Loading
Loading
@@ -8,6 +8,7 @@
#import "iTermBackgroundColorRenderer.h"
#import "iTermBadgeRenderer.h"
#import "iTermBroadcastStripesRenderer.h"
#import "iTermCopyBackgroundRenderer.h"
#import "iTermCursorGuideRenderer.h"
#import "iTermCursorRenderer.h"
#import "iTermMetalFrameData.h"
Loading
Loading
@@ -41,7 +42,9 @@ static const NSInteger iTermMetalDriverMaximumNumberOfFramesInFlight = 3;
iTermCursorRenderer *_underlineCursorRenderer;
iTermCursorRenderer *_barCursorRenderer;
iTermCursorRenderer *_blockCursorRenderer;
iTermCursorRenderer *_frameCursorRenderer;
iTermCopyModeCursorRenderer *_copyModeCursorRenderer;
iTermCopyBackgroundRenderer *_copyBackgroundRenderer;
 
// The command Queue from which we'll obtain command buffers
id<MTLCommandQueue> _commandQueue;
Loading
Loading
@@ -81,7 +84,10 @@ static const NSInteger iTermMetalDriverMaximumNumberOfFramesInFlight = 3;
_underlineCursorRenderer = [iTermCursorRenderer newUnderlineCursorRendererWithDevice:mtkView.device];
_barCursorRenderer = [iTermCursorRenderer newBarCursorRendererWithDevice:mtkView.device];
_blockCursorRenderer = [iTermCursorRenderer newBlockCursorRendererWithDevice:mtkView.device];
_frameCursorRenderer = [iTermCursorRenderer newFrameCursorRendererWithDevice:mtkView.device];
_copyModeCursorRenderer = [iTermCursorRenderer newCopyModeCursorRendererWithDevice:mtkView.device];
_copyBackgroundRenderer = [[iTermCopyBackgroundRenderer alloc] initWithDevice:mtkView.device];
_commandQueue = [mtkView.device newCommandQueue];
_queue = dispatch_queue_create("com.iterm2.metalDriver", NULL);
_currentFrames = [NSMutableArray array];
Loading
Loading
@@ -165,13 +171,14 @@ static const NSInteger iTermMetalDriverMaximumNumberOfFramesInFlight = 3;
}
 
#pragma mark - Drawing
// Called on the main queue
- (iTermMetalFrameData *)newFrameData {
iTermMetalFrameData *frameData = [[iTermMetalFrameData alloc] init];
frameData.perFrameState = [_dataSource metalDriverWillBeginDrawingFrame];
frameData.transientStates = [NSMutableDictionary dictionary];
frameData.rows = [NSMutableArray array];
frameData.gridSize = VT100GridSizeMake(_columns, _rows);
frameData.gridSize = frameData.perFrameState.gridSize;
frameData.scale = _scale;
return frameData;
}
Loading
Loading
@@ -180,40 +187,67 @@ static const NSInteger iTermMetalDriverMaximumNumberOfFramesInFlight = 3;
view:(nonnull MTKView *)view {
dispatch_group_t group = dispatch_group_create();
id <MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];
iTermRenderConfiguration *configuration = [[iTermRenderConfiguration alloc] initWithViewportSize:_viewportSize scale:frameData.scale];
CGFloat blending;
BOOL tiled;
NSImage *backgroundImage = [frameData.perFrameState metalBackgroundImageGetBlending:&blending tiled:&tiled];
[_backgroundImageRenderer setImage:backgroundImage blending:blending tiled:tiled];
if (backgroundImage) {
frameData.intermediateRenderPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor];
MTLRenderPassColorAttachmentDescriptor *colorAttachment = frameData.intermediateRenderPassDescriptor.colorAttachments[0];
colorAttachment.storeAction = MTLStoreActionStore;
MTLTextureDescriptor *textureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:frameData.drawable.texture.pixelFormat
width:frameData.drawable.texture.width
height:frameData.drawable.texture.height
mipmapped:NO];
textureDescriptor.usage = (MTLTextureUsageShaderRead |
MTLTextureUsageShaderWrite |
MTLTextureUsageRenderTarget |
MTLTextureUsagePixelFormatView);
colorAttachment.texture = [frameData.device newTextureWithDescriptor:textureDescriptor];
colorAttachment.texture.label = @"Intermediate Texture";
} else {
frameData.intermediateRenderPassDescriptor = nil;
}
 
[frameData prepareWithBlock:^{
[commandBuffer enqueue];
commandBuffer.label = @"Draw Terminal";
[self.nonCellRenderers enumerateObjectsUsingBlock:^(id<iTermMetalRenderer> _Nonnull renderer, NSUInteger idx, BOOL * _Nonnull stop) {
dispatch_group_enter(group);
[renderer createTransientStateForViewportSize:_viewportSize
commandBuffer:commandBuffer
completion:^(__kindof iTermMetalRendererTransientState * _Nonnull tState) {
if (tState) {
frameData.transientStates[NSStringFromClass(renderer.class)] = tState;
[self updateRenderer:renderer
state:tState
perFrameState:frameData.perFrameState];
}
dispatch_group_leave(group);
}];
[renderer createTransientStateForConfiguration:configuration
commandBuffer:commandBuffer
completion:^(__kindof iTermMetalRendererTransientState * _Nonnull tState) {
if (tState) {
frameData.transientStates[NSStringFromClass(renderer.class)] = tState;
[self updateRenderer:renderer
state:tState
perFrameState:frameData.perFrameState];
}
dispatch_group_leave(group);
}];
}];
VT100GridSize gridSize = frameData.gridSize;
iTermCellRenderConfiguration *cellConfiguration = [[iTermCellRenderConfiguration alloc] initWithViewportSize:_viewportSize
scale:frameData.scale
cellSize:_cellSize
gridSize:gridSize
usingIntermediatePass:(frameData.intermediateRenderPassDescriptor != nil)];
[self.cellRenderers enumerateObjectsUsingBlock:^(id<iTermMetalCellRenderer> _Nonnull renderer, NSUInteger idx, BOOL * _Nonnull stop) {
dispatch_group_enter(group);
[renderer createTransientStateForViewportSize:_viewportSize
cellSize:_cellSize
gridSize:gridSize
commandBuffer:commandBuffer
completion:^(__kindof iTermMetalCellRendererTransientState * _Nonnull tState) {
if (tState) {
frameData.transientStates[NSStringFromClass([renderer class])] = tState;
[self updateRenderer:renderer
state:tState
perFrameState:frameData.perFrameState];
}
dispatch_group_leave(group);
}];
[renderer createTransientStateForCellConfiguration:cellConfiguration
commandBuffer:commandBuffer
completion:^(__kindof iTermMetalCellRendererTransientState * _Nonnull tState) {
if (tState) {
frameData.transientStates[NSStringFromClass([renderer class])] = tState;
[self updateRenderer:renderer
state:tState
perFrameState:frameData.perFrameState];
}
dispatch_group_leave(group);
}];
}];
 
// Renderers may not yet have transient state
Loading
Loading
@@ -247,16 +281,14 @@ static const NSInteger iTermMetalDriverMaximumNumberOfFramesInFlight = 3;
// Called when all renderers have transient state
- (void)finalizeRenderersWithFrameData:(iTermMetalFrameData *)frameData {
// TODO: call setMarkStyle:row: for each mark
iTermTextRendererTransientState *textState =
frameData.transientStates[NSStringFromClass([_textRenderer class])];
iTermBackgroundColorRendererTransientState *backgroundState =
frameData.transientStates[NSStringFromClass([_backgroundColorRenderer class])];
CGSize cellSize = textState.cellSize;
CGFloat scale = frameData.scale;
// Copy state
iTermCopyBackgroundRendererTransientState *copyState =
frameData.transientStates[NSStringFromClass([_copyBackgroundRenderer class])];
copyState.sourceTexture = frameData.intermediateRenderPassDescriptor.colorAttachments[0].texture;
 
// Update glyph attributes for block cursor if needed.
iTermMetalCursorInfo *cursorInfo = [frameData.perFrameState metalDriverCursorInfo];
if (cursorInfo.cursorVisible && cursorInfo.shouldDrawText) {
NSLog(@"Cursor on line %d of display", cursorInfo.coord.y);
if (!cursorInfo.frameOnly && cursorInfo.cursorVisible && cursorInfo.shouldDrawText) {
iTermMetalRowData *rowWithCursor = frameData.rows[cursorInfo.coord.y];
iTermMetalGlyphAttributes *glyphAttributes = (iTermMetalGlyphAttributes *)rowWithCursor.attributesData.mutableBytes;
glyphAttributes[cursorInfo.coord.x].foregroundColor = cursorInfo.textColor;
Loading
Loading
@@ -265,6 +297,18 @@ static const NSInteger iTermMetalDriverMaximumNumberOfFramesInFlight = 3;
cursorInfo.cursorColor.blueComponent,
1);
}
// Update the text renderer's transient state with current glyphs and colors.
CGFloat scale = frameData.scale;
iTermTextRendererTransientState *textState =
frameData.transientStates[NSStringFromClass([_textRenderer class])];
// Set the background texture if one is available.
textState.backgroundTexture = frameData.intermediateRenderPassDescriptor.colorAttachments[0].texture;
CGSize cellSize = textState.cellConfiguration.cellSize;
iTermBackgroundColorRendererTransientState *backgroundState =
frameData.transientStates[NSStringFromClass([_backgroundColorRenderer class])];
[frameData.rows enumerateObjectsUsingBlock:^(iTermMetalRowData * _Nonnull rowData, NSUInteger idx, BOOL * _Nonnull stop) {
iTermMetalGlyphKey *glyphKeys = (iTermMetalGlyphKey *)rowData.keysData.mutableBytes;
[textState setGlyphKeysData:rowData.keysData
Loading
Loading
@@ -284,13 +328,26 @@ static const NSInteger iTermMetalDriverMaximumNumberOfFramesInFlight = 3;
[textState willDraw];
}
 
- (void)drawRenderer:(id<iTermMetalRenderer>)renderer
frameData:(iTermMetalFrameData *)frameData
renderEncoder:(id<MTLRenderCommandEncoder>)renderEncoder {
NSString *className = NSStringFromClass([renderer class]);
iTermMetalRendererTransientState *state = frameData.transientStates[className];
assert(state);
if (!state.skipRenderer) {
[renderer drawWithRenderEncoder:renderEncoder transientState:state];
}
}
- (void)drawCellRenderer:(id<iTermMetalCellRenderer>)renderer
frameData:(iTermMetalFrameData *)frameData
renderEncoder:(id<MTLRenderCommandEncoder>)renderEncoder {
NSString *className = NSStringFromClass([renderer class]);
iTermMetalCellRendererTransientState *state = frameData.transientStates[className];
assert(state);
[renderer drawWithRenderEncoder:renderEncoder transientState:state];
if (!state.skipRenderer) {
[renderer drawWithRenderEncoder:renderEncoder transientState:state];
}
}
 
- (void)reallyDrawInView:(MTKView *)view
Loading
Loading
@@ -298,10 +355,11 @@ static const NSInteger iTermMetalDriverMaximumNumberOfFramesInFlight = 3;
commandBuffer:(id<MTLCommandBuffer>)commandBuffer {
DLog(@" Really drawing");
 
MTLRenderPassDescriptor *renderPassDescriptor = frameData.renderPassDescriptor;
MTLRenderPassDescriptor *renderPassDescriptor = frameData.intermediateRenderPassDescriptor ?: frameData.renderPassDescriptor;
id<MTLRenderCommandEncoder> renderEncoder;
if (renderPassDescriptor != nil) {
id<MTLRenderCommandEncoder> renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
renderEncoder.label = @"Render Terminal";
renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
renderEncoder.label = frameData.intermediateRenderPassDescriptor ? @"Render background to intermediate" : @"Render All Layers of Terminal";
frameData.drawable.texture.label = @"Drawable";
 
// Set the region of the drawable to which we'll draw.
Loading
Loading
@@ -315,10 +373,14 @@ static const NSInteger iTermMetalDriverMaximumNumberOfFramesInFlight = 3;
};
[renderEncoder setViewport:viewport];
 
// [_backgroundImageRenderer drawWithRenderEncoder:renderEncoder];
[self drawRenderer:_backgroundImageRenderer
frameData:frameData
renderEncoder:renderEncoder];
[self drawCellRenderer:_backgroundColorRenderer
frameData:frameData
renderEncoder:renderEncoder];
// [_broadcastStripesRenderer drawWithRenderEncoder:renderEncoder];
// [_badgeRenderer drawWithRenderEncoder:renderEncoder];
// [_cursorGuideRenderer drawWithRenderEncoder:renderEncoder];
Loading
Loading
@@ -333,9 +395,15 @@ static const NSInteger iTermMetalDriverMaximumNumberOfFramesInFlight = 3;
renderEncoder:renderEncoder];
break;
case CURSOR_BOX:
[self drawCellRenderer:_blockCursorRenderer
frameData:frameData
renderEncoder:renderEncoder];
if (cursorInfo.frameOnly) {
[self drawCellRenderer:_frameCursorRenderer
frameData:frameData
renderEncoder:renderEncoder];
} else {
[self drawCellRenderer:_blockCursorRenderer
frameData:frameData
renderEncoder:renderEncoder];
}
break;
case CURSOR_VERTICAL:
[self drawCellRenderer:_barCursorRenderer
Loading
Loading
@@ -349,6 +417,28 @@ static const NSInteger iTermMetalDriverMaximumNumberOfFramesInFlight = 3;
 
// [_copyModeCursorRenderer drawWithRenderEncoder:renderEncoder];
 
if (frameData.intermediateRenderPassDescriptor) {
[renderEncoder endEncoding];
}
}
renderPassDescriptor = frameData.renderPassDescriptor;
if (renderPassDescriptor) {
if (frameData.intermediateRenderPassDescriptor) {
renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:frameData.renderPassDescriptor];
renderEncoder.label = @"Copy bg and render text";
// Set the region of the drawable to which we'll draw.
MTLViewport viewport = {
-(double)_viewportSize.x,
0.0,
_viewportSize.x * 2,
_viewportSize.y * 2,
-1.0,
1.0
};
[renderEncoder setViewport:viewport];
[self drawRenderer:_copyBackgroundRenderer frameData:frameData renderEncoder:renderEncoder];
}
[self drawCellRenderer:_textRenderer
frameData:frameData
renderEncoder:renderEncoder];
Loading
Loading
@@ -428,7 +518,7 @@ static const NSInteger iTermMetalDriverMaximumNumberOfFramesInFlight = 3;
#pragma mark - Updating
 
- (void)updateRenderer:(id)renderer
state:(iTermMetalRendererTransientState *)tState
state:(__kindof iTermMetalRendererTransientState *)tState
perFrameState:(id<iTermMetalDriverDataSourcePerFrameState>)perFrameState {
if (renderer == _backgroundImageRenderer) {
[self updateBackgroundImageRendererWithPerFrameState:perFrameState];
Loading
Loading
@@ -443,7 +533,9 @@ static const NSInteger iTermMetalDriverMaximumNumberOfFramesInFlight = 3;
[self updateCursorGuideRendererWithPerFrameState:perFrameState];
} else if (renderer == _underlineCursorRenderer ||
renderer == _barCursorRenderer ||
renderer == _blockCursorRenderer) {
renderer == _blockCursorRenderer ||
renderer == _frameCursorRenderer ||
renderer == _copyBackgroundRenderer) {
[self updateCursorRendererWithPerFrameState:perFrameState];
} else if (renderer == _copyModeCursorRenderer) {
[self updateCopyModeCursorRendererWithPerFrameState:perFrameState];
Loading
Loading
@@ -475,6 +567,8 @@ static const NSInteger iTermMetalDriverMaximumNumberOfFramesInFlight = 3;
case CURSOR_BOX:
[_blockCursorRenderer setCoord:cursorInfo.coord];
[_blockCursorRenderer setColor:cursorInfo.cursorColor];
[_frameCursorRenderer setCoord:cursorInfo.coord];
[_frameCursorRenderer setColor:cursorInfo.cursorColor];
break;
case CURSOR_VERTICAL:
[_barCursorRenderer setCoord:cursorInfo.coord];
Loading
Loading
@@ -501,13 +595,15 @@ static const NSInteger iTermMetalDriverMaximumNumberOfFramesInFlight = 3;
_underlineCursorRenderer,
_barCursorRenderer,
_blockCursorRenderer,
_frameCursorRenderer,
_copyModeCursorRenderer ];
}
 
- (NSArray<id<iTermMetalRenderer>> *)nonCellRenderers {
return @[ _backgroundImageRenderer,
_badgeRenderer,
_broadcastStripesRenderer ];
_broadcastStripesRenderer,
_copyBackgroundRenderer ];
}
 
- (void)scheduleDrawIfNeededInView:(MTKView *)view {
Loading
Loading
Loading
Loading
@@ -70,6 +70,4 @@ typedef NSDictionary iTermHotKeyDescriptor;
// Compares pointers only
- (BOOL)isExactlyEqualToDictionary:(NSDictionary *)other;
 
- (NSDictionary *)dictionaryBySettingObject:(id)object forKey:(NSString *)key;
@end
Loading
Loading
@@ -319,10 +319,4 @@ static const NSEventModifierFlags iTermHotkeyModifierMask = (NSEventModifierFlag
return result;
}
 
- (NSDictionary *)dictionaryBySettingObject:(id)object forKey:(NSString *)key {
NSMutableDictionary *mutableSelf = [[self mutableCopy] autorelease];
mutableSelf[key] = object;
return mutableSelf;
}
@end
Loading
Loading
@@ -6334,6 +6334,10 @@ ITERM_WEAKLY_REFERENCEABLE
}
}
 
- (NSImage *)textViewBackgroundImage {
return _backgroundImage;
}
- (NSColor *)processedBackgroundColor {
NSColor *unprocessedColor = [_colorMap colorForKey:kColorMapBackground];
return [_colorMap processedBackgroundColorForBackgroundColor:unprocessedColor];
Loading
Loading
Loading
Loading
@@ -194,6 +194,8 @@ typedef NS_ENUM(NSInteger, PTYTextViewSelectionExtensionUnit) {
- (void)textViewDidSelectRangeForFindOnPage:(VT100GridCoordRange)range;
- (void)textViewNeedsDisplayInRect:(NSRect)rect;
- (void)textViewDidSelectPasswordPrompt;
- (NSImage *)textViewBackgroundImage;
- (BOOL)backgroundImageTiled;
 
@end
 
Loading
Loading
Loading
Loading
@@ -89,7 +89,7 @@
#import "TmuxDashboardController.h"
#import "ToastWindowController.h"
#import "VT100Terminal.h"
#import "iTermSubpixelModelBuilder.h"
#import <Quartz/Quartz.h>
#import <objc/runtime.h>
 
Loading
Loading
Loading
Loading
@@ -6,7 +6,7 @@
//
 
#import <Foundation/Foundation.h>
#import <Metal/Metal.h>
#import "iTermPreciseTimer.h"
#import "VT100GridTypes.h"
 
Loading
Loading
@@ -42,6 +42,11 @@ extern void iTermMetalFrameDataStatsBundleAdd(iTermMetalFrameDataStatsBundle *de
@property (atomic, strong) NSString *status;
@property (nonatomic, strong) MTLRenderPassDescriptor *renderPassDescriptor;
@property (nonatomic, strong) id<CAMetalDrawable> drawable;
@property (nonatomic, strong) id<MTLDevice> device;
// If nonnil then all draw stages before text draw with encoders from this render pass descriptor.
// It will have a texture identical to the drawable's texture.
@property (nonatomic, strong) MTLRenderPassDescriptor *intermediateRenderPassDescriptor;
 
- (void)loadFromView:(MTKView *)view;
- (void)prepareWithBlock:(void (^)(void))block;
Loading
Loading
Loading
Loading
@@ -55,6 +55,8 @@ static NSInteger gNextFrameDataNumber;
}
 
- (void)loadFromView:(MTKView *)view {
self.device = view.device;
iTermPreciseTimerStatsStartTimer(&_stats.getCurrentDrawableStats);
self.drawable = view.currentDrawable;
iTermPreciseTimerStatsMeasureAndRecordTimer(&_stats.getCurrentDrawableStats);
Loading
Loading
Loading
Loading
@@ -97,6 +97,10 @@ static NSColor *ColorForVector(vector_float4 v) {
BOOL _blinkingItemsVisible;
NSRange _inputMethodMarkedRange;
NSTimeInterval _timeSinceCursorMoved;
CGFloat _backgroundImageBlending;
BOOL _backgroundImageTiled;
NSImage *_backgroundImage;
}
 
- (instancetype)initWithTextView:(PTYTextView *)textView
Loading
Loading
@@ -188,10 +192,6 @@ static NSColor *ColorForVector(vector_float4 v) {
}
_cursorInfo = [[iTermMetalCursorInfo alloc] init];
#warning TODO: blinking cursor
NSLog(@"Visible range is [%d, %d), have %d lines of scrollback",
(int)_visibleRange.start.y,
(int)_visibleRange.end.y,
(int)_numberOfScrollbackLines);
NSInteger lineWithCursor = textView.dataSource.cursorY - 1 + _numberOfScrollbackLines;
if ([self shouldDrawCursor] &&
textView.cursorVisible &&
Loading
Loading
@@ -199,7 +199,6 @@ static NSColor *ColorForVector(vector_float4 v) {
lineWithCursor + 1 < _visibleRange.end.y) {
const int offset = _visibleRange.start.y - _numberOfScrollbackLines;
_cursorInfo.cursorVisible = YES;
NSLog(@"Cursor is visible on line %d (%d of visible region)", (int)lineWithCursor, (int)(lineWithCursor - _visibleRange.start.y));
_cursorInfo.type = textView.drawingHelper.cursorType;
_cursorInfo.coord = VT100GridCoordMake(textView.dataSource.cursorX - 1,
textView.dataSource.cursorY - 1 - offset);
Loading
Loading
@@ -233,18 +232,32 @@ static NSColor *ColorForVector(vector_float4 v) {
}
}
} else {
NSLog(@"Cursor is not visible");
_cursorInfo.cursorVisible = NO;
}
_backgroundImageBlending = textView.blend;
_backgroundImageTiled = textView.delegate.backgroundImageTiled;
_backgroundImage = [textView.delegate textViewBackgroundImage];
}
return self;
}
 
- (VT100GridSize)gridSize {
return _gridSize;
}
// Private queue
- (nullable iTermMetalCursorInfo *)metalDriverCursorInfo {
return _cursorInfo;
}
 
// Private queue
- (NSImage *)metalBackgroundImageGetBlending:(CGFloat *)blending tiled:(BOOL *)tiled {
*blending = _backgroundImageBlending;
*tiled = _backgroundImageTiled;
return _backgroundImage;
}
// Private queue
- (void)metalGetGlyphKeys:(iTermMetalGlyphKey *)glyphKeys
attributes:(iTermMetalGlyphAttributes *)attributes
Loading
Loading
@@ -287,6 +300,15 @@ static NSColor *ColorForVector(vector_float4 v) {
vector_float4 unprocessed = [self unprocessedColorForBackgroundColorKey:&backgroundKey];
// The unprocessed color is needed for minimum contrast computation for text color.
background[x] = [_colorMap fastProcessedBackgroundColorForBackgroundColor:unprocessed];
if (_backgroundImage) {
// This is kind of ugly but it simplifies things a lot to do it
// here. The alpha value for background colors should be 1
// except when there's a background image, in which case the
// default background color gets a user-defined alpha value.
const BOOL isDefaultBackgroundColor = (backgroundKey.bgColorMode == ColorModeAlternate &&
backgroundKey.bgColor == ALTSEM_DEFAULT);
background[x].w = isDefaultBackgroundColor ? (1 - _backgroundImageBlending) : 1;
}
}
lastBackgroundKey = backgroundKey;
attributes[x].backgroundColor = background[x];
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment