OpenGL ES 2.0 blending - opengl-es-2.0

I set glBlendFunc to
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
and for testing purposes I set the color in my fragment shader like this
gl_FragColor = vec4(1.0,0.0,0.0,0.0);
Shouldn't the object be fully transparent? What could the reason be if it's not?

The first argument of glBlendFunc() is the source factor, the second is the destination factor. In your case:
sfactor = 1.0;
dfactor = 1.0 - src.alpha;
being src.alpha = 0.0, from your gl_FragColor:
sfactor = 1.0;
dfactor = 1.0;
So the color put to the buffer will be:
buffer = sfactor * src + dfactor * dst;
Substituting...
buffer = (1.0,0.0,0.0,0.0) + dst;
So, putting it simple, you are adding 1 to the red channel of the existing buffer.
If you want to make the output fully transparent, the usual function is:
glBlendFunc(GL_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
The one you wrote is usually used for pre-multiplied alpha in the source. But (1, 0, 0, 0) is obviously not a premultiplied alpha value!

Related

CARenderer draws nothing into bound texture

I'm trying to setup a CARenderer to draw into a mtlTexture, but all my attempts to get this working in a playground don't draw anything.
The resulting image is solid red, the yellow layer doesn't seem to be rendered at all.
Here's the simplest version of what I've tried:
import QuartzCore
import Metal
let layerTest = CATextLayer()
layerTest.frame = .init(origin: .zero, size: .init(width: 1920, height: 1080))
layerTest.string = "TEST"
layerTest.foregroundColor = .black
layerTest.backgroundColor = CGColor(red: 1.0, green: 1.0, blue: 0.0, alpha: 1.0)
layerTest.position = CGPoint(x:0.0, y:0.0)
layerTest.anchorPoint = CGPoint(x:0.0, y:0.0)
layerTest.masksToBounds = true
let device = MTLCreateSystemDefaultDevice()!
let context = CIContext(mtlDevice: device)
let textureDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .rgba8Unorm, width: 1920, height: 1080, mipmapped: false)
textureDescriptor.usage = [.unknown]
textureDescriptor.storageMode = .private
let bytes = UnsafeMutablePointer<UInt8>.allocate(capacity: 1920 * 1080 * 4)
//fill the buffer with red
let pattern = UnsafeMutablePointer<UInt8>.allocate(capacity: 4)
(pattern + 0).initialize(to: 255)
(pattern + 1).initialize(to: 0)
(pattern + 2).initialize(to: 0)
(pattern + 3).initialize(to: 255)
memset_pattern4(bytes, pattern, 1920 * 1080 * 4)
let mtlBuffer = device.makeBuffer(bytes: bytes, length: 1920 * 1080 * 4)!
let mtlTexture = mtlBuffer.makeTexture(descriptor: textureDescriptor, offset: 0, bytesPerRow: 1920 * 4)!
let render = CARenderer(mtlTexture: mtlTexture)
render.bounds = layerTest.frame
render.layer = layerTest
render.setDestination(mtlTexture)
render.beginFrame(atTime: CACurrentMediaTime(), timeStamp: nil)
render.addUpdate(render.bounds)
render.render()
render.endFrame()
let ciImage = CIImage(mtlTexture: mtlTexture)!
let cgImage: CGImage = context.createCGImage(ciImage, from: ciImage.extent)! //<- this is just red frame
I submitted this to apple DTS and got a reply:
Setting the root layer of the CARenderer requires one implicit CATransaction to transfer ownership of the layer tree to the CARenderer’s context. CARenderer frame render methods will not work correctly until this ownership transfer is complete.
To complete the current transaction we call flush() and then commit() it:
render.layer = layerTest
CATransaction.flush()
CATransaction.commit()
Adding these two lines resolved the issue for me.

MacOS MTKView metal self.device.newBufferWithBytes crashes with assert

I want to draw a simple triangle and it crashes after I am trying to create MTLBuffer.
static float vertexes[] = {
0.0, 0.5, 0.0,
-0.5f, -0.5f, 0.0,
0.5, -0.5f, 0.0
};
id <MTLBuffer> buffer = [self.device newBufferWithBytes:vertexes
length:sizeof(vertexes) options:MTLResourceStorageModePrivate];
Here is the assert:
-[MTLDebugDevice newBufferWithBytes:length:options:]:392: failed assertion `storageModePrivate incompatible with ...WithBytes variant of newBuffer'
So how to create a buffer from the vertexes using MTLResourceStorageModePrivate option?
You must create a temporary blit buffer and use it to copy the contents to the private buffer. Here's example code:
buffer = [self.device newBufferWithLength:sizeof( vertexes )
options:MTLResourceStorageModePrivate];
id<MTLBuffer> blitBuffer = [self.device newBufferWithBytes:vertexes
length:sizeof( vertexes )
options:MTLResourceCPUCacheModeDefaultCache];
id <MTLCommandBuffer> cmd_buffer = [commandQueue commandBuffer];
id <MTLBlitCommandEncoder> blit_encoder = [cmd_buffer blitCommandEncoder];
[blit_encoder copyFromBuffer:blitBuffer
sourceOffset:0
toBuffer:buffer
destinationOffset:0
size:sizeof( vertexes )];
[blit_encoder endEncoding];
[cmd_buffer commit];
[cmd_buffer waitUntilCompleted];

Fastest way to draw offscreen CALayer content

I'm looking for the fastest way to draw offscreen CALayer content (no alpha needed) on macOS. Note, that these examples aren't threaded, but the point is (and why I'm not just using CALayer.setNeedsDisplay) because I'm doing this drawing on a background thread.
My original code did this:
let bounds = layer.bounds.size
let contents = NSImage(size: size)
contents.lockFocusFlipped(true)
let context = NSGraphicsContext.current()!.cgContext
layer.draw(in: context)
contents.unlockFocus()
layer.contents = contents
My current best is quite a bit faster:
let contentsScale = layer.contentsScale
let width = Int(bounds.width * contentsScale)
let height = Int(bounds.height * contentsScale)
let bytesPerRow = width * 4
let alignedBytesPerRow = ((bytesPerRow + (64 - 1)) / 64) * 64
let context = CGContext(
data: nil,
width: width,
height: height,
bitsPerComponent: 8,
bytesPerRow: alignedBytesPerRow,
space: NSScreen.main()?.colorSpace?.cgColorSpace ?? CGColorSpaceCreateDeviceRGB(),
bitmapInfo: CGImageAlphaInfo.noneSkipLast.rawValue
)!
context.scaleBy(x: contentsScale, y: contentsScale)
layer.draw(in: context)
layer.contents = context.makeImage()
Tips and recommendations for making it better/faster are welcome.

UIImageEffects: white image when Gaussian radius above 280, vImageBoxConvolve_ARGB8888 issue?

I'm using the Gaussian blur algorithm found in Apple's UIImageEffects example:
CGFloat inputRadius = blurRadius * inputImageScale;
if (inputRadius - 2. < __FLT_EPSILON__)
inputRadius = 2.;
uint32_t radius = floor((inputRadius * 3. * sqrt(2 * M_PI) / 4 + 0.5) / 2);
radius |= 1; // force radius to be odd so that the three box-blur methodology works.
NSInteger tempBufferSize = vImageBoxConvolve_ARGB8888(inputBuffer, outputBuffer, NULL, 0, 0, radius, radius, NULL, kvImageGetTempBufferSize | kvImageEdgeExtend);
void *tempBuffer = malloc(tempBufferSize);
vImageBoxConvolve_ARGB8888(inputBuffer, outputBuffer, tempBuffer, 0, 0, radius, radius, NULL, kvImageEdgeExtend);
vImageBoxConvolve_ARGB8888(outputBuffer, inputBuffer, tempBuffer, 0, 0, radius, radius, NULL, kvImageEdgeExtend);
vImageBoxConvolve_ARGB8888(inputBuffer, outputBuffer, tempBuffer, 0, 0, radius, radius, NULL, kvImageEdgeExtend);
free(tempBuffer);
vImage_Buffer *temp = inputBuffer;
inputBuffer = outputBuffer;
outputBuffer = temp;
I'm also working with some fairly large images. Unfortunately, when the radius gets over 280, the blurred image suddenly becomes almost completely blank, regardless of the resolution. What's going on here? Does vImageBoxConvolve_ARGB8888 have an undocumented kernel width/height limit? Or does it have to do with the way the box kernel width is computed from the radius?
EDIT:
Found a similar question here: vImageBoxConvolve: errors when kernel size > 255. A Gaussian radius of 280 roughly translates to a 260 size kernel, so that part matches up.
The box and tent convolves can run into a problem where the value modulo overflows the 31-bit accumulator. However 255 seems a bit narrow for that. There should be another 7 bits of headroom at least for 255x255. Certainly, check the error code returned by the function. If it says everything is fine, then this seems bug worthy. Attach some sample code to help Apple reproduce the problem to help ensure it is fixed.

How do I use the scanCrop property of a ZBar reader?

I am using the ZBar SDK for iPhone in order to scan a barcode. I want the reader to scan only a specific rectangle instead of the whole view, for doing that it is needed to set the scanCrop property of the reader to the desired rectangle.
I'm having hard time with understanding the rectangle parameter that has to be set.
Can someone please tell me what rect should I give as an argument if on portrait view its coordinates would be: CGRectMake( A, B, C, D )?
From the zbar's ZBarReaderView Class documentation :
CGRect scanCrop
The region of the video image that will be scanned, in normalized image coordinates. Note that the video image is in landscape mode (default {{0, 0}, {1, 1}})
The coordinates for all of the arguments is in a normalized float, which is from 0 - 1. So, in normalized value, theView.width is 1.0, and theView.height is 1.0. Therefore, the default rect is {{0,0},{1,1}}.
So for example, if I have a transparent UIView named scanView as a scanning region for my readerView. Rather than do :
readerView.scanCrop = scanView.frame;
We should do this, normalizing every arguments first :
CGFloat x,y,width,height;
x = scanView.frame.origin.x / readerView.bounds.size.width;
y = scanView.frame.origin.y / readerView.bounds.size.height;
width = scanView.frame.size.width / readerView.bounds.size.width;
height = scanView.frame.size.height / readerView.bounds.size.height;
readerView.scanCrop = CGRectMake(x, y, width, height);
It works for me. Hope that helps.
You can use scan crop area by doing this.
reader.scanCrop = CGRectMake(x,y,width,height);
for eg.
reader.scanCrop = CGRectMake(.25,0.25,0.5,0.45);
I used this and its working for me.
come on!!! this is the right way to adjust the crop area;
I had wasted tons of time on it;
readerView.scanCrop = [self getScanCrop:cropRect readerViewBounds:contentView.bounds];
- (CGRect)getScanCrop:(CGRect)rect readerViewBounds:(CGRect)rvBounds{
CGFloat x,y,width,height;
x = rect.origin.y / rvBounds.size.height;
y = 1 - (rect.origin.x + rect.size.width) / rvBounds.size.width;
width = rect.size.height / rvBounds.size.height;
height = rect.size.width / rvBounds.size.width;
return CGRectMake(x, y, width, height);
}